pax_global_header00006660000000000000000000000064151420513000014501gustar00rootroot0000000000000052 comment=ae50ff5a86b248af8ea81b368e25bc3931b72312 ceccomp-4.0/000077500000000000000000000000001514205130000127555ustar00rootroot00000000000000ceccomp-4.0/.clang-format000066400000000000000000000000221514205130000153220ustar00rootroot00000000000000BasedOnStyle: GNU ceccomp-4.0/.github/000077500000000000000000000000001514205130000143155ustar00rootroot00000000000000ceccomp-4.0/.github/workflows/000077500000000000000000000000001514205130000163525ustar00rootroot00000000000000ceccomp-4.0/.github/workflows/new-release.yml000066400000000000000000000017271514205130000213130ustar00rootroot00000000000000name: New Release on: workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v5 - name: Find the latest tag run: | git fetch --tags LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) echo "The latest tag is:" $LATEST_TAG echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV - name: Generate Changelog run: | echo "# What changed" > ${{ github.workspace }}-CHANGELOG.md python3 scripts/release-changelog.py $LATEST_TAG >> ${{ github.workspace }}-CHANGELOG.md - name: Preview Changelog run: cat ${{ github.workspace }}-CHANGELOG.md - name: Release uses: softprops/action-gh-release@v2 with: body_path: ${{ github.workspace }}-CHANGELOG.md repository: dbgbgtf1/Ceccomp tag_name: ${{ env.LATEST_TAG }} token: ${{ secrets.GITHUB_TOKEN }} ceccomp-4.0/.gitignore000066400000000000000000000004371514205130000147510ustar00rootroot00000000000000build/ __pycache__ compile_commands.json a.out Test result* *.gdb_history *.cache/ CeccompBuild/ diff_result /Makefile po/*.po~ SOURCES.txt include/config.h py-temp/dist/ py-temp/build/ py-temp/ceccomp.egg-info/ py-temp/wheelhouse py-temp/ceccomp/ceccomp test_prog text gdb_scripts *.mo ceccomp-4.0/CHANGELOG.md000066400000000000000000000155161514205130000145760ustar00rootroot00000000000000# CHANGELOG Items marked with :star: is the major change why we release a new version. ## 4.1 (INCOMING) ## 4.0 * **BREAKING** :star: :fire: **We aggressively refactored our code and the asm grammar is a bit different from v3. Please refer to documentation for new grammar.** :fire: * :star: Add seize subfunction in trace pid :link: [#23] * :star: Remove `-mno-omit-leaf-frame-pointer` flag if not supported by cc * :star: Fix asm and disasm endianness problems :link: [#22] * :star: Handle '>=' '>' '<=' '<' correctly :link: [#25] * :star: Find python by `env` for compatibility in `configure` :heart: [@tesuji] :link: [!19] * :star: Migrate to pytest for testing * :star: Migrate to po4a for documentation translation * Print messages in dynamic tests * Add `-q` for trace to suppress `[INFO]` output * Reject text that are error ones for kernel :link: [#17] * Print info when trace found failed seccomp syscall * Support `-` to refer to stdin or stdout * Allow returning numbers or `$A` * Fixed a typo in README :heart: [@k4lizen] :link: [!28] [#22]: https://github.com/dbgbgtf1/Ceccomp/issues/22 [#25]: https://github.com/dbgbgtf1/Ceccomp/issues/25 [#23]: https://github.com/dbgbgtf1/Ceccomp/issues/23 [#17]: https://github.com/dbgbgtf1/Ceccomp/issues/17 [@tesuji]: https://github.com/tesuji [!19]: https://github.com/dbgbgtf1/Ceccomp/pulls/19 [@k4lizen]: https://github.com/k4lizen [!28]: https://github.com/dbgbgtf1/Ceccomp/pulls/28 ## 3.5 * **BREAKING** :star: Now `trace` and `probe` no more accepts `-a`; arch is automatically retrieved via ptrace * :star: Add `i686` arch support * :star: Handle unknown system arch * Fix 32-bit ceccomp `strtol` error * Generalize test script to for different archs * `trace` now follows tracee's arch instead of system's * Fix Makefile implicit rules ## 3.4 * :star: Fix cross arch trace problems :link: [#16] * :star: Add support for NO_COLOR env check :link: [#15] * :star: Fix heap oob write if `goto` exceeds bpf range * :star: Correctly kill children processes when recved signals * :star: Fix potential invalid `%s` access in `ALU` and `ST_STX` * Fix ambiguous `idx` in `ST_STX` * Fix color display in emu * Use `git describe --long` to have unified name pattern in `configure` devmode * Add more tests to check ceccomp behaviour [#15]: https://github.com/dbgbgtf1/Ceccomp/issues/15 [#16]: https://github.com/dbgbgtf1/Ceccomp/issues/16 ## 3.3 * :star: Add `--enable-static` in `configure` to build statically * Portable release in pip, for x86_64, i386, aarch64, armhf, riscv64 * Fix not-null-terminated argp option array * Fix copyright as GPLv3 or later ## 3.2 * **BREAKING** :star: Enhance arch/syscall prediction :link: [#12] * :star: Add support for Debian build system :link: [#13] * :star: Improve syntax highlighting * Hide unused bpf operation in emu :link: [#14] * Fix `CECCOMP_USAGE` warning as it's a function call * Add a dark-mode css for html doc * Sort source object for reproducible build [#13]: https://github.com/dbgbgtf1/Ceccomp/issues/13 [#12]: https://github.com/dbgbgtf1/Ceccomp/issues/12 [#14]: https://github.com/dbgbgtf1/Ceccomp/issues/14 ## 3.1 * **BREAKING** :star: Add support for multiple process tracing :link: [#3] * :star: Cover all cases of trace pid errors :link: [#11] * :star: Add support for internationalization/localization :link: [#1] * :star: Fix `ld` error when compiling on Kali Linux * Fix potential misuse of `Info` in trace * Improve log system and related color management * Fix output file may not be opened correctly * Add a GitHub action to issue new release easily [#3]: https://github.com/dbgbgtf1/Ceccomp/issues/3 [#11]: https://github.com/dbgbgtf1/Ceccomp/issues/11 [#1]: https://github.com/dbgbgtf1/Ceccomp/issues/1 ## 3.0 * **BREAKING** Remove `-o FILE` for `trace` pid mode * :star: Add doc system powered by *asciidoc* :link: [#2] * :star: Add signal forwarding to `trace` in expected way :link: [#5] * :star: Add subcommand description :link: [#7] * :star: Add color option, can be set to always, auto, never :link: [#10] * :star: Add Python-version configure script :link: [#9] * Warn invalid seccomp filter instead of refuse directly :link: [#4] * Raise errors on corrupted TEXT in `emu` * Suppress printing function if not debugging * Set `$?` to 1 when failed to parse args * Limit truncating file on `-o` flag by checking subcommand * Update color in `return $A` BPF OP for better visual effect * Implement assigning `len(struct seccomp_data)` to `A` or `X` * Implement uninstall operation and verbose control in Makefile [#2]: https://github.com/dbgbgtf1/Ceccomp/issues/2 [#5]: https://github.com/dbgbgtf1/Ceccomp/issues/5 [#7]: https://github.com/dbgbgtf1/Ceccomp/issues/7 [#10]: https://github.com/dbgbgtf1/Ceccomp/issues/10 [#4]: https://github.com/dbgbgtf1/Ceccomp/issues/4 [#9]: https://github.com/dbgbgtf1/Ceccomp/issues/9 ## 2.9 * :star: Fix Makefile compatibility among shells :link: [#6] * Add git-hook to remind dev to update version string * Update color in `return` BPF OP for better visual effect [#6]: https://github.com/dbgbgtf1/Ceccomp/issues/6 ## 2.8 * :star: Improve compatibility among compilers * :star: Add Kbuild-like build prompt with progress * Improve logging implementation ## 2.7 * :star: Fix `trace` and `probe` foreground interaction issue * Refactor method to terminate children ## 2.6 * :star: Fix Makefile link order (should be put in the end of line) * Suppress compiler warning by `// fall through` * Applying more checks on `asm` ## 2.5 * :star: Add parentheses for `TRAP` as it has `ret_data`, but *libseccomp* hasn't implement it :link: [seccomp/libseccomp#466] * Add check script * Fix uninitialized memory access [seccomp/libseccomp#466]: https://github.com/seccomp/libseccomp/issues/466 ## 2.4 * :star: Port kernel filter check * Add some logging functions ## 2.3 * Fix `asm` lacking `ALU_NEG` operation * Fix `asm` hanging if ## 2.2 * Fix `emu` emulates wrong ALU operations * Better help message ## 2.1 * Remove `CXX` in Makefile, now it's purely in C * Fix Makefile typo ## 2.0 * :star: Turn to `argp` to parse arguments * :star: Add `-o` mode to output filters * :star: Use C to implement `parsefilter` ## 1.5 * :star: New probe mode to test common syscalls instantly * Adapt completion script to latest code base ## 1.4 * Fix lacking `\n` in trace ## 1.3 * `trace` now accept pid to grab filters * Zsh completion script is capable of completing pid ## 1.2 * :star: Ready for PKGBUILD to build package * Fix zsh completion script ## 1.1 * First release version * Add basic zsh completion script ceccomp-4.0/CONTRIBUTING.md000066400000000000000000000017661514205130000152200ustar00rootroot00000000000000# Contribute to ceccomp ## Found a bug Raise an issue directly, write your expected behavior and what was wrong in ceccomp. Attach a minimum reproducible input and your version information (ceccomp and libseccomp). We may close your issue if the bug is not reproducible. ## Want a new feature Raise an issue to tell us what you want. You may write your expected output. Please wait patiently as we may not have spare time to implement it and may close it. We are glad to see your Pull Request :smile:. ## New pull request If you are able to fix some bugs or import some new features, you could open a new pull request. As for new feature, it's recommend to open an issue first to confirm your feature is reasonable. NOTE, don't write code with AI completely, we may close it directly. Run `clang-format` to format your code. The config is under repo root and should be recognized by `clang-format` automatically. Once your pull request is merged, you know that your code is licensed under GPL v3 or later. ceccomp-4.0/LICENSE000066400000000000000000001045151514205130000137700ustar00rootroot00000000000000 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 . ceccomp-4.0/Makefile.in000066400000000000000000000200731514205130000150240ustar00rootroot00000000000000ifneq ($(words $(MAKECMDGOALS)),1) # if no argument was given to make... .DEFAULT_GOAL = all %: # define a last resort default rule @$(MAKE) $@ --no-print-directory -rRf $(firstword $(MAKEFILE_LIST)) # recursive make call, else ifndef ECHO T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \ -nrRf $(firstword $(MAKEFILE_LIST)) \ ECHO="COUNTTHIS" | grep -c "COUNTTHIS") L := $(shell echo -n $T | wc -m) ifeq ($(NO_COLOR),) GREEN := $(shell printf '\033[32m') RESET := $(shell printf '\033[0m') endif ECHO_NOPROG = printf " $(1)\t$(2)\n" ECHO = printf " %-12s[%$Ld/%$Ld]\t$(2)\n" \ $(1) \ $(shell SHELL=$(SHELL) flock $(LOCK) -c 'read n < $(MARK); echo $$n; echo $$((n+1)) > $(MARK)') \ $T endif # end for progress block, but not for else in line 5 V := {{VERBOSE}} ifeq ($V,1) Q := ECHO := ECHO_NOPROG := else Q := @ endif VERSION := {{VERSION}} TAG_TIME := {{TAG_TIME}} ARCH := $(shell uname -m) TARGET := ceccomp test SRC_DIR := src INC_DIR := include TEST_DIR := test BUILD_DIR := build BUILD_UTIL := $(BUILD_DIR)/utils BUILD_LEXICAL := $(BUILD_DIR)/lexical BUILD_RESOLVER := $(BUILD_DIR)/resolver BUILD_DECODER := $(BUILD_DIR)/decoder LOCK := $(BUILD_DIR)/lock MARK := $(BUILD_DIR)/progress C_SRCS := $(shell find $(SRC_DIR) ! -name 'ceccomp.c' -name '*.c' -or -name '*.s' | LC_ALL=C sort) INC_SRCS := $(shell find $(INC_DIR) -name '*.h' | LC_ALL=C sort) TEST_SRCS := $(TEST_DIR)/unit_test.c OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.c.o,$(C_SRCS)) TEST_OBJS := $(patsubst $(TEST_DIR)/%.c,$(BUILD_DIR)/%.c.o,$(TEST_SRCS)) {{DOC_IF}} DOC_DIR := docs IMG_DIR := images SRC_IMG_DIR := $(DOC_DIR)/$(IMG_DIR) BUILD_IMG_DIR := $(BUILD_DIR)/$(IMG_DIR) IMG_SRCS := $(shell find $(SRC_IMG_DIR) -name "*.webp") {{DOC_ENDIF}} CECCOMP_MAIN := $(BUILD_DIR)/ceccomp.c.o CECCOMP_C := $(SRC_DIR)/ceccomp.c CC := {{CC}} LOCALE_DIR := {{LOCALEDIR}} CFLAGS := -fpie -fstack-protector -Wall -Wextra {{SYS_INC_DIR}} {{EXTRA_CFLAGS}} LDFLAGS := -z now -z noexecstack -fpie -fstack-protector -Wall -Wextra {{SYS_LIB_DIR}} -lseccomp {{LIBARGP}} {{EXTRA_LDFLAGS}} DEBUG := {{DEBUG_LEVEL}} ifdef DEBUG ifeq ($(DEBUG),1) CFLAGS += -g -O2 -fno-omit-frame-pointer {{ARCH_PRESERVE_FP}} LDFLAGS += -g -O2 else ifeq ($(DEBUG),2) CFLAGS += -g3 -O0 -DDEBUG # O0 preserve fp by default LDFLAGS += -g3 -O0 endif else CFLAGS += -O2 LDFLAGS += -O2 -s endif STATIC := {{IS_STATIC}} ifeq ($(STATIC), 1) CFLAGS += -static LDFLAGS += -static endif all: ceccomp {{DOC_IF}}doc{{DOC_ENDIF}} {{I18N_IF}}mo{{I18N_ENDIF}} {{I18N_IF}} ##### INTERNATIONALIZATION I18N_DIR := po PO_ZHCN := $(I18N_DIR)/zh_CN.po MO_ZHCN := $(patsubst $(I18N_DIR)/%.po,$(BUILD_DIR)/%.mo,$(PO_ZHCN)) update-po: $Q$(MAKE) -C $(I18N_DIR) --no-print-directory Q=$Q $(MO_ZHCN): $(PO_ZHCN) | $(BUILD_DIR) $(MARK) $Q$(call ECHO,MSGFMT,$@) $Qmsgfmt -o $@ $< mo: $(MO_ZHCN) $Q$(call ECHO_NOPROG,$(GREEN)BUILT,mo$(RESET)) .PHONY: update-po mo {{I18N_ENDIF}} ##### DOCUMENTATION PART ##### {{DOC_IF}} MAN_OBJ := $(BUILD_DIR)/ceccomp.1 MAN_CHN := $(BUILD_DIR)/cn/ceccomp.1 HTML_OBJ := $(BUILD_DIR)/ceccomp.html HTML_CHN := $(BUILD_DIR)/ceccomp-cn.html ASCIIDOC := {{ASCIIDOC}} doc: doc_html doc_man doc_man: $(MAN_OBJ) $(MAN_CHN) $Q$(call ECHO_NOPROG,$(GREEN)BUILT,man doc$(RESET)) doc_html: $(HTML_OBJ) $(HTML_CHN) $Q$(call ECHO_NOPROG,$(GREEN)BUILT,html doc$(RESET)) $(MAN_OBJ): $(DOC_DIR)/ceccomp.adoc | $(BUILD_DIR) $(MARK) $Q$(call ECHO,ADOC,$@) $Q$(ASCIIDOC) -b manpage $< -a VERSION=$(VERSION) -a ARCH=$(ARCH) -a TAG_TIME=$(TAG_TIME) -o $@ $(MAN_CHN): $(DOC_DIR)/ceccomp.zh_CN.adoc | $(BUILD_DIR) $(MARK) $Q$(call ECHO,ADOC,$@) $Q$(ASCIIDOC) -b manpage $< -a VERSION=$(VERSION) -a ARCH=$(ARCH) -a TAG_TIME=$(TAG_TIME) -o $@ $(HTML_OBJ): $(DOC_DIR)/ceccomp.adoc | $(BUILD_DIR) $(MARK) $Q$(call ECHO,ADOC,$@) $Q$(ASCIIDOC) -b html5 $< -a VERSION=$(VERSION) -a ARCH=$(ARCH) -a TAG_TIME=$(TAG_TIME) -o $@ $(HTML_CHN): $(DOC_DIR)/ceccomp.zh_CN.adoc | $(BUILD_DIR) $(MARK) $Q$(call ECHO,ADOC,$@) $Q$(ASCIIDOC) -b html5 $< -a VERSION=$(VERSION) -a ARCH=$(ARCH) -a TAG_TIME=$(TAG_TIME) -o $@ .PHONY: doc doc_man doc_html {{DOC_ENDIF}} ##### INSTALL CECCOMP ##### DESTDIR := {{DESTDIR}} PREFIX := {{PREFIX}} BIN_DIR := {{BINDIR}} ZSH_FPATH := {{ZSH_FPATH}} ZSH_SRC := completions install: bin_install zsh_cmp_install {{DOC_IF}}doc_install{{DOC_ENDIF}} {{I18N_IF}}mo_install{{I18N_ENDIF}} bin_install: ceccomp $(BUILD_DIR)/ceccomp $Q$(call ECHO_NOPROG,INSTALL,$< $(BIN_DIR)) $Qinstall -Dt $(DESTDIR)$(BIN_DIR) $(BUILD_DIR)/ceccomp zsh_cmp_install: $(ZSH_SRC)/_ceccomp $Q$(call ECHO_NOPROG,INSTALL,$< $(ZSH_FPATH)) $Qinstall -Dm 0644 -t $(DESTDIR)$(ZSH_FPATH) $< .PHONY: bin_install zsh_cmp_install install {{DOC_IF}} MAN_DIR := {{MANDIR}} MAN_DST := $(MAN_DIR)/man1 MAN_CHN_DST := $(MAN_DIR)/zh_CN/man1 DOC_INSTALL_DIR := {{DOCDIR}} HTML_DST := $(DOC_INSTALL_DIR)/html doc_install: doc doc_man_install doc_html_install {{I18N_IF}}doc_man_cn_install{{I18N_ENDIF}} doc_images_install $Q$(call ECHO_NOPROG,INSTALL,README.md $(DOC_INSTALL_DIR)) $Qinstall -Dm 0644 -t $(DESTDIR)$(DOC_INSTALL_DIR) README.md doc_man_install: $(MAN_OBJ) $Q$(call ECHO_NOPROG,INSTALL,$< $(MAN_DST)) $Qinstall -Dm 0644 -t $(DESTDIR)$(MAN_DST) $< doc_man_cn_install: $(MAN_CHN) $Q$(call ECHO_NOPROG,INSTALL,$< $(MAN_CHN_DST)) $Qinstall -Dm 0644 -t $(DESTDIR)$(MAN_CHN_DST) $< doc_html_install: $(HTML_OBJ) $(HTML_CHN) $Q$(call ECHO_NOPROG,INSTALL,$< $(HTML_DST)) $Qinstall -Dm 0644 -t $(DESTDIR)$(HTML_DST) $^ doc_images_install: $(IMG_SRCS) $Q$(call ECHO_NOPROG,INSTALL,images $(HTML_DST)) $Qinstall -Dm 0644 -t $(DESTDIR)$(HTML_DST)/images $^ .PHONY: doc_man_install doc_html_install doc_man_cn_install doc_images_install doc_install {{DOC_ENDIF}} {{I18N_IF}} mo_install: $(MO_ZHCN) $Q$(call ECHO_NOPROG,INSTALL,$< $(LOCALE_DIR)) $Qinstall -Dm 0644 $< $(patsubst $(BUILD_DIR)/%.mo,$(DESTDIR)$(LOCALE_DIR)/%/LC_MESSAGES/ceccomp.mo,$<) .PHONY: mo_install {{I18N_ENDIF}} UNINSTALL = test -f $(1) && rm -f $(1) || : UNINSTALL_RECURSIVE = test -d $(1) && rm -rf $(1) || : uninstall: $Q$(call ECHO_NOPROG,UNINSTALL,$(BIN_DIR)/ceccomp) $Q$(call UNINSTALL,$(DESTDIR)$(BIN_DIR)/ceccomp) $Q$(call ECHO_NOPROG,UNINSTALL,$(ZSH_FPATH)/_ceccomp) $Q$(call UNINSTALL,$(DESTDIR)$(ZSH_FPATH)/_ceccomp) {{DOC_IF}} $Q$(call ECHO_NOPROG,UNINSTALL,$(MAN_DIR)/man1/ceccomp.1) $Q$(call UNINSTALL,$(DESTDIR)$(MAN_DIR)/man1/ceccomp.1) $Q$(call ECHO_NOPROG,UNINSTALL,$(MAN_DIR)/zh_CN/man1/ceccomp.1) $Q$(call UNINSTALL,$(DESTDIR)$(MAN_DIR)/zh_CN/man1/ceccomp.1) $Q$(call ECHO_NOPROG,UNINSTALL,$(DOC_INSTALL_DIR)) $Q$(call UNINSTALL_RECURSIVE,$(DESTDIR)$(DOC_INSTALL_DIR)/../ceccomp) # in case DOC_INSTALL_DIR is not set... {{DOC_ENDIF}} .PHONY: uninstall #### C OBJECTS ### CONFIG_HEADER := $(INC_DIR)/config.h $(CONFIG_HEADER): Makefile | $(MARK) $Q$(call ECHO,SED,$@) $Qsed 's/@@VERSION@@/$(VERSION)/;s/@@TAG_TIME@@/$(TAG_TIME)/;s/@@BUILDER@@/{{BUILDER}}/;{{LOCALE_SED}}' \ $(CONFIG_HEADER).in > $(CONFIG_HEADER) ceccomp: $(BUILD_DIR)/ceccomp $Q$(call ECHO_NOPROG,$(GREEN)BUILT,$@$(RESET)) $(BUILD_DIR)/ceccomp: $(OBJS) $(CECCOMP_MAIN) | $(MARK) $Q$(call ECHO,LD,$@) $Q$(CC) $^ -o $@ $(LDFLAGS) test: $(BUILD_DIR)/test $Q$(call ECHO_NOPROG,$(GREEN)BUILT,$@$(RESET)) $(BUILD_DIR)/test: $(TEST_OBJS) | $(MARK) $Q$(call ECHO,LD,$@) $Q$(CC) $^ -o $@ $(LDFLAGS) $(BUILD_DIR)/ceccomp.c.o: $(SRC_DIR)/ceccomp.c $(CONFIG_HEADER) | $(BUILD_DIR) $(MARK) $Q$(call ECHO,CC,$@) $Q$(CC) -I$(INC_DIR) $(CFLAGS) $< -c -o $@ $(BUILD_DIR)/%.c.o: $(SRC_DIR)/%.c | $(BUILD_DIR) $(MARK) $(CONFIG_HEADER) $Q$(call ECHO,CC,$@) $Q$(CC) -I$(INC_DIR) $(CFLAGS) $< -c -o $@ $(BUILD_DIR)/%.c.o: $(TEST_DIR)/%.c | $(BUILD_DIR) $(MARK) $Q$(call ECHO,CC,$@) $Q$(CC) -I$(INC_DIR) $(CFLAGS) $< -c -o $@ $(BUILD_DIR): $Q$(call ECHO_NOPROG,MKDIR,$(BUILD_DIR)) $Qmkdir -p $(BUILD_DIR) $(BUILD_UTIL) $(BUILD_LEXICAL) $(BUILD_RESOLVER) $(BUILD_TEST) $(BUILD_IMG_DIR) $(BUILD_DECODER) FORCE: $(MARK): FORCE | $(BUILD_DIR) $Qecho 1 > $(MARK) .PHONY: clean all ceccomp test clean: $Q$(call ECHO_NOPROG,RM,$(BUILD_DIR)) $Qrm -rf $(BUILD_DIR) $Q$(call ECHO_NOPROG,RM,$(CONFIG_HEADER)) $Qrm $(CONFIG_HEADER) endif ceccomp-4.0/README.md000066400000000000000000000071401514205130000142360ustar00rootroot00000000000000# Ceccomp A tool to analyze seccomp filters like `seccomp-tools`, written in C ## Features - :gear: Robust assembler and disassembler - :blue_book: Complete documentation - :1234: Various architecture support powered by libseccomp - :globe_with_meridians: Multi-language support - :feather: Minimum build depencies for core binary - :paintbrush: Enhanced syntax highlighting - :100: Informational error messages - :shell: Powerful Zshell completion - :no_entry_sign: Pure C without LLM-generated garbage ## Doc & Screenshots [English Version](docs/ceccomp.adoc) | [中文文档](docs/ceccomp.zh_CN.adoc) ## Install - Arch Linux users: Install via AUR, build `ceccomp` package ⇒ [![AUR package](https://repology.org/badge/version-for-repo/aur/ceccomp.svg)](https://repology.org/project/ceccomp/versions) Or install via `archlinuxcn` repo if you have it set in you `pacman.conf`. - Debian, Ubuntu or Kali users: ceccomp is available with `apt` now if you are using distros below: [![Debian testing](https://repology.org/badge/version-for-repo/debian_14/ceccomp.svg?header=Debian%20testing)](https://repology.org/project/ceccomp/versions) [![Debian unstable](https://repology.org/badge/version-for-repo/debian_unstable/ceccomp.svg?header=Debian%20unstable)](https://repology.org/project/ceccomp/versions) [![Ubuntu 26.04](https://repology.org/badge/version-for-repo/ubuntu_26_04/ceccomp.svg?header=Ubuntu%2026.04)](https://repology.org/project/ceccomp/versions) [![Kali Linux](https://repology.org/badge/version-for-repo/kali_rolling/ceccomp.svg?header=Kali%20Linux)](https://repology.org/project/ceccomp/versions) - NixOS users: @tesuji helps us submit a PR at NixOS, but it's blocked as nobody cares... If you like our software, please :+1: in NixOS/nixpkgs#462592 to help ceccomp into nixpkgs! - Stable installation: Clone the whole repo, then run `./configure`. Add `--without-doc` flag if you don't have `asciidoctor`, and add `--without-i18n` flag if you don't have `gettext` package. ```sh git clone https://github.com/dbgbgtf1/Ceccomp.git cd Ceccomp ./configure ./configure # run this again if Makefile is not generated make make install # install at /usr/bin ``` - Testing installation: Clone the whole repo, and then run `./configure --devmode`. ```sh git clone https://github.com/dbgbgtf1/Ceccomp.git cd Ceccomp ./configure --devmode make ``` ## Run Test Run configure and make, then invoke `pytest test` from repo root. Trace pid case will be skipped if no CAP_SYS_ADMIN. If you find some checks failed, please submit an issue to report your case. To run the test, you need 2 extra packages: `pkgconf` (required by `pkg-config`) and `python-pytest` (required by `pytest`). ## CheatSheet image ## Credits - [seccomp-tools](https://github.com/david942j/seccomp-tools): The tool in Ruby inspires us to write ceccomp - [Bootswatch](https://bootswatch.com/slate/): Provides awesome css for html doc under MIT - [Linux kernel](https://github.com/torvalds/linux): Port some bpf checks - [Verstable](https://github.com/JacksonAllan/Verstable): High-performance hash table implementation in C - [a5hash](https://github.com/avaneev/a5hash): High-performance hash implementation for short strings in C Any Issue or PR are welcome! :heart: Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details. ## License Copyright (C) 2025-present, ceccomp contributors, distributed under GNU General Public License v3.0 or Later ceccomp-4.0/SECURITY.md000066400000000000000000000014451514205130000145520ustar00rootroot00000000000000# Security Policy ## Reporting a Vulnerability If you found a severe bug, for instance, hijack user's computer via a bpf filter, please send emails to both of us (ma2014119@outlook.com and dudududumaxver@outlook.com). If we don't reply to you in 3 days, you could find us via Discord (@rocketmadev and @dbgbgtf) in `pwndbg` server, please dm to us directly. If neither of us don't reply back in 2 weeks, just open an issue. When sending us your bug report, please confirm that the severe bug you found is reproducible and survive in latest version of ceccomp. Then write about how to trigger the bug (a PoC). We will contact you later. NOTE that if you are sending us AI-generated bug report without check or spams, we may refuse your request. Once we confirm your bug, we may help you issue a CVE. ceccomp-4.0/completions/000077500000000000000000000000001514205130000153115ustar00rootroot00000000000000ceccomp-4.0/completions/_ceccomp000066400000000000000000000112571514205130000170120ustar00rootroot00000000000000#compdef ceccomp # Zsh completion script for ceccomp local -a subcmd options expl arch_compstr output_compstr local state local archs=( 'x86_64' 'i386' 'x32' 'aarch64' 'arm' 'loongarch64' 'm68k' 'mips' 'mipsel' 'mips64' 'mipsel64' 'mips64n32' 'mipsel64n32' 'parisc' 'parisc64' 'ppc64' 'ppc64le' 'ppc' 's390x' 's390' 'riscv64' ) arch_compstr=( '(-a --arch)'{-a,--arch}"[Target BPF architecture]:ARCH:($archs)" ) color_compstr=( '(-c --color)'{-c,--color}'[When to display in color]:WHEN:(always auto never)' ) output_compstr=( '(-o --output)'{-o,--output}'[Print to file to avoid mixing tracee output]:FILE:_files' ) local syscalls=( 'open' 'read' 'write' 'close' 'mmap' 'mprotect' 'execve' 'execveat' 'pread64' 'readv' 'writev' 'preadv' 'preadv2' 'openat' 'openat2' 'sendfile64' 'send' 'sendto' 'sendmsg' 'recv' 'recvfrom' 'recvmsg' 'io_uring_setup' 'io_uring_enter' 'io_uring_register' 'ptrace' ) if (( CURRENT == 2 )) { subcmd=( 'asm:Assemble bpf text to raw bytes' 'disasm:Disassemble raw bytes to bpf text' 'trace:Run program or trace pid, extract bpf filter and then print to text' 'emu:Emulate bpf program with given syscall and bpf text' 'probe:Trace the program for the first filter and emulate common syscalls' 'help:Display ceccomp help information' 'version:Display ceccomp version' ) _describe 'subcmd' subcmd return } elif (( CURRENT > 2 )) { case $words[2] { (asm) _arguments \ $arch_compstr \ $color_compstr \ '(-f --fmt)'{-f,--fmt}'[Output format of BPF]:FMT:(raw hexline hexfmt)' \ '2:BPF:_files' \ '*: :' ;; (disasm) _arguments \ $arch_compstr \ $color_compstr \ '2:RAW:_files' \ '*: :' ;; (trace) _arguments -C \ $color_compstr \ '(-p --pid)'{-p,--pid}'[Attach to which process to extract its filters]:PID:->getpid' \ '(-q --quiet)'{-q,--quiet}'[Print warning and error messages only]' \ '(-s --seize)'{-s,--seize}'[Follow pid to trace load-filter operation (pid mode)]' \ $output_compstr \ '*:arguments:_files' # complete non-kernel pids like kill if [[ $state == 'getpid' ]] { local line pids lines pids=() # extract command output line by line lines=("${(@f)$(ps --ppid 2 -p 2 -N -o pid=,tty=,user=,comm=)}") for line ($lines) { pids+=${line[(w)1]} # extract first word (pid) } _wanted nonk-pids expl 'non-kernel process ID' \ compadd -o nosort -ld lines -a pids } ;; (probe) _arguments -C \ $color_compstr \ $output_compstr \ '(-q --quiet)'{-q,--quiet}'[Print warning and error messages only]' \ '*:arguments:_files' ;; (emu) _arguments -C \ $arch_compstr \ $color_compstr \ '(-q --quiet)'{-q,--quiet}'[Print return value only]' \ '2:BPF:_files' \ ':NR:->syscall_nr' \ ':ARGV0:->argv0' \ ':ARGV1:->argv1' \ ':ARGV2:->argv2' \ ':ARGV3:->argv3' \ ':ARGV4:->argv4' \ ':ARGV5:->argv5' \ ':IP:->ip' \ '*: :' case $state { (syscall_nr) _message -r 'Hint: syscall_nr or syscall_name (to name but a few)' _values SYSCALL $syscalls ;; (argv0) _message -r 'Hint: u64 for argv[0]' ;; (argv1) _message -r 'Hint: u64 for argv[1]' ;; (argv2) _message -r 'Hint: u64 for argv[2]' ;; (argv3) _message -r 'Hint: u64 for argv[3]' ;; (argv4) _message -r 'Hint: u64 for argv[4]' ;; (argv5) _message -r 'Hint: u64 for argv[5]' ;; (ip) _message -r 'Hint: u64 for instruction pointer' ;; } ;; (*) # help and version has no completion available return ;; } } ceccomp-4.0/configure000077500000000000000000000631161514205130000146730ustar00rootroot00000000000000#!/usr/bin/env python3 import datetime import argparse import subprocess import os import sys import shutil from collections.abc import Callable from dataclasses import dataclass # constants VERSION = '4.0' TAG_TIME = '2026-2-8' # color escape code definition if not os.getenv('NO_COLOR'): def TXT_RED(s): return f'\x1b[31m{s}\x1b[0m' def TXT_GREEN(s): return f'\x1b[32m{s}\x1b[0m' def TXT_YELLOW(s): return f'\x1b[33m{s}\x1b[0m' def TXT_BLUE(s): return f'\x1b[34m{s}\x1b[0m' def TXT_MAGENTA(s): return f'\x1b[35m{s}\x1b[0m' def TXT_CYAN(s): return f'\x1b[36m{s}\x1b[0m' def TXT_GRAY(s): return f'\x1b[90m{s}\x1b[0m' else: def TXT_RED(s): return s def TXT_GREEN(s): return s def TXT_YELLOW(s): return s def TXT_BLUE(s): return s def TXT_MAGENTA(s): return s def TXT_CYAN(s): return s def TXT_GRAY(s): return s # error handling def warn(msg: any): print(TXT_YELLOW(f'[WARN] {msg}'), flush=True, file=sys.stderr) def error(msg: any): print(TXT_RED(f'[ERR!] {msg}'), file=sys.stderr) sys.exit(1) # build option parsing class GNUStyleHelpFormatter(argparse.RawDescriptionHelpFormatter): """Custom help formatter that forces --opt=VALUE style""" def _format_action_invocation(self, action): # only long options, no short option if not action.option_strings: return super()._format_action_invocation(action) return ', '.join(f'{opt}={action.metavar}' if action.metavar else opt for opt in action.option_strings) def parse_args() -> tuple[argparse.Namespace, list[str]]: epilog = '''Some influential environment variables: CC C compiler command CFLAGS Extra C compiler flags LDFLAGS Extra linker flags ASCIIDOC Asciidoc document compiler Please report bug at https://github.com/dbgbgtf1/Ceccomp Copyright (C) 2025-present, distributed under GPLv3 or later ''' parser = argparse.ArgumentParser( prog='configure.py', description='Configure build flags for Ceccomp, written in Python', epilog=epilog, formatter_class=GNUStyleHelpFormatter, ) config_grp = parser.add_argument_group('Configure-time options (fixed once Makefile is generated)') config_grp.add_argument('--enable-verbose', action='store_true', help='Enable verbose output') config_grp.add_argument('--without-doc', action='store_true', help='Do not install documentation') config_grp.add_argument('--without-i18n', action='store_true', help='Do not generate locale mo files') config_grp.add_argument('--devmode', action='store_true', help='Allow you to compile against any commit (only work in git mode)') config_grp.add_argument('--packager', type=str, metavar='BY', default='manual', help='Who build the package (default: manual)') config_grp.add_argument('--disable-option-checking', action='store_true', help='Whether to throw an error when unknown options found') config_grp.add_argument('--build', type=str, metavar='TARGET', help='Which target to build against, optional. For instance, x86_64-linux-gnu') config_grp.add_argument('--includedir', type=str, metavar='DIR', help='Optional system include directory') config_grp.add_argument('--libdir', type=str, metavar='DIR', help='Optional system library directory') # argp is dynamically tested make_grp = parser.add_argument_group('Make-time options (can be overridden by make cli) [make var]') make_grp.add_argument('--prefix', type=str, metavar='PREFIX', default='/usr', help='Installation prefix (default: /usr) [PREFIX]') make_grp.add_argument('--bindir', type=str, metavar='DIR', help='User executables directory (default: PREFIX/bin) [BIN_DIR]') make_grp.add_argument('--zshfpath', type=str, metavar='DIR', help='Zsh functions path (default: PREFIX/share/zsh/site-functions) [ZSH_FPATH]') make_grp.add_argument('--docdir', type=str, metavar='DIR', help='Documentation directory (default: PREFIX/share/doc/ceccomp) [DOC_DIR]') make_grp.add_argument('--mandir', type=str, metavar='DIR', help='Man pages directory (default: PREFIX/share/man) [MAN_DIR]') make_grp.add_argument('--localedir', type=str, metavar='DIR', help='Localization directory (default: PREFIX/share/locale) [LOCALE_DIR]') make_grp.add_argument('--destdir', type=str, metavar='DIR', help='Optional dir to install software, useful for package managers [DESTDIR]') make_grp.add_argument('--debug-level', type=int, metavar='LEVEL', default=1, help='Set debug symbol level (0: -O2 -s|1: -O2 -g, default|2: -O0 -g3 -DDEBUG) [DEBUG]') make_grp.add_argument('--version', type=str, metavar='VER', help=f'Set ceccomp version (default: {VERSION}) [VERSION]') make_grp.add_argument('--tag-time', type=str, metavar='TIME', help=f'Set the time of current tag. Use "CONFIG" for the time when running configure; or write in YYYY-mm-dd like default value {TAG_TIME} [TAG_TIME]') make_grp.add_argument('--enable-static', action='store_true', help='Enable static build [STATIC]') return parser.parse_known_args() @dataclass class BuildOptions: prefix: str bindir: str zshfpath: str docdir: str mandir: str localedir: str destdir: str debug_level: int version: str tag_time: str is_static: bool verbose: bool doc: bool i18n: bool devmode: bool builder: str check_option: bool target: str sys_includedir: str sys_libdir: str cc: str | None asciidoc: str | None cflags: str ldflags: str unknown_options: list[str] git_install: bool = True def __post_init__(self): if self.bindir is None: self.bindir = os.path.join(self.prefix, 'bin') if self.zshfpath is None: self.zshfpath = os.path.join(self.prefix, 'share', 'zsh', 'site-functions') if self.docdir is None: self.docdir = os.path.join(self.prefix, 'share', 'doc', 'ceccomp') if self.mandir is None: self.mandir = os.path.join(self.prefix, 'share', 'man') if self.localedir is None: self.localedir = os.path.join(self.prefix, 'share', 'locale') def replace_prefix(dirent: str | None) -> str: return dirent.replace('${prefix}', self.prefix) if dirent else '' self.bindir = replace_prefix(self.bindir) self.zshfpath = replace_prefix(self.zshfpath) self.docdir = replace_prefix(self.docdir) self.mandir = replace_prefix(self.mandir) self.localedir = replace_prefix(self.localedir) self.destdir = replace_prefix(self.destdir) self.sys_includedir = replace_prefix(self.sys_includedir) self.sys_libdir = replace_prefix(self.sys_libdir) if self.debug_level not in (0, 1, 2): error('debug_level option can only be 0, 1 or 2!') def check_cmd(env: str, *candidates: list[str]) -> str | None: """enumerate available command in system""" var = getattr(self, env) if var is not None: if shutil.which(var) is None: error(f'{var} provided by environment {env.upper()} is not runnable!') else: return var # no such environment variable for candidate in candidates: if shutil.which(candidate) is not None: return candidate return None # the None value will be processed in check if self.target: self.cc = check_cmd('cc', f'{self.target}-cc', f'{self.target}-gcc', f'{self.target}-clang') if not self.cc: warn(f'No C compiler with prefix {self.target} found') self.cc = check_cmd('cc', 'cc', 'gcc', 'clang') else: # cc may be passed from environ, so we shall not merge the logic self.cc = check_cmd('cc', 'cc', 'gcc', 'clang') self.asciidoc = check_cmd('asciidoc', 'asciidoctor') if self.cflags is None: self.cflags = '' if self.ldflags is None: self.ldflags = '' if self.tag_time == 'CONFIG': self.tag_time = datetime.datetime.today().strftime('%Y-%m-%d') elif self.tag_time: try: datetime.datetime.strptime(self.tag_time, '%Y-%m-%d') except ValueError: error(f'Invalid tag-time {self.tag_time}, format is YYYY-mm-dd') # check the options if self.unknown_options: if self.check_option: error(f'Unrecognized options: {" ".join(self.unknown_options)}, please pass --help to see correct options!') else: for opt in self.unknown_options: print(TXT_GRAY(f'Ignoring unknown option {opt}')) warn(f'Discarding {len(self.unknown_options)} unknown options!') args, unknown = parse_args() buildopts = BuildOptions( prefix=args.prefix, bindir=args.bindir, zshfpath=args.zshfpath, docdir=args.docdir, mandir=args.mandir, localedir=args.localedir, destdir=args.destdir, debug_level=args.debug_level, version=args.version, tag_time=args.tag_time, is_static=args.enable_static, verbose=args.enable_verbose, doc=not args.without_doc, i18n=not args.without_i18n, devmode=args.devmode, builder=args.packager, check_option=not args.disable_option_checking, target=args.build, sys_includedir=args.includedir, sys_libdir=args.libdir, cc=os.getenv('CC'), asciidoc=os.getenv('ASCIIDOC'), cflags=os.getenv('CFLAGS'), ldflags=os.getenv('LDFLAGS'), unknown_options=unknown, ) del args, unknown # gathering system information and check @dataclass class Makefile: s: str def inject(self, placeholder: str, filler: str | list[str] | bool) -> None: if isinstance(filler, str): self.s = self.s.replace(f'{{{{{placeholder}}}}}', filler) # {{PLACEHOLDER}} return if isinstance(filler, bool): # automatically loop from 0 to perform substitution keep = filler i = 0 while True: if self.s.find(f'{{{{{placeholder}_IF}}}}') == -1: break if keep: self.s = self.s.replace(f'{{{{{placeholder}_IF}}}}', '') # {{PLACEHOLDER_IF}} self.s = self.s.replace(f'{{{{{placeholder}_ENDIF}}}}', '') else: begin = self.s.find(f'{{{{{placeholder}_IF}}}}') end = self.s.find(f'{{{{{placeholder}_ENDIF}}}}') assert begin != -1 assert end != -1 end += len(f'{{{{{placeholder}_ENDIF}}}}') self.s = self.s[:begin] + self.s[end:] i += 1 return for i, e in enumerate(filler): self.s = self.s.replace(f'{{{{{placeholder}{i}}}}}', e) # {{PLACEHOLDER0}} class TaskManager: tasks: list[Callable[[Makefile], None]] makefile: Makefile def __init__(self, makefile_name: str) -> None: self.tasks = [] if not os.path.isfile(makefile_name): error(f'Reading Makefile {makefile_name}, but it\'s not a file!') try: with open(makefile_name) as file: self.makefile = Makefile(file.read()) except OSError as e: error(f'Can not open Makefile: {e}') def run_tasks(self): width = len(str(len(self.tasks))) total = len(self.tasks) for seq, handler in enumerate(self.tasks, start=1): # no need to flush as stdout will be flushed in handler print(f'[{seq:>{width}}/{total}] ', end='') handler(self.makefile) def add_task(self, handler: Callable[[Makefile], None]): self.tasks.append(handler) taskmgr = TaskManager('Makefile.in') def pcheck(txt: str): print(f'Checking {txt}... ', end='', flush=True) def run_command(argv: list[str], stdin: str | None=None, push_sys_dir: bool=False) -> tuple[int, str, str]: """ Run command with given argv and input, return process returncode, stdout and stderr Return -1, '', exception message if exception raised is known, or else return -2, '', message. """ try: argv[0] = shutil.which(argv[0]) if push_sys_dir: # using C compiler! if buildopts.sys_includedir: # in case some include/lib is not accessible for current compiler argv.append(f'-I{buildopts.sys_includedir}') if buildopts.sys_libdir: argv.append(f'-L{buildopts.sys_libdir}') if buildopts.is_static: argv.append('-static') proc = subprocess.run(argv, input=stdin, capture_output=True, text=True) except subprocess.SubprocessError as e: return -1, '', str(e) except UnicodeDecodeError as e: return -1, '', 'UnicodeDecodeError: program output contains non-utf-8 bytes' except Exception as e: return -2, '', str(e) else: return proc.returncode, proc.stdout, proc.stderr def new_task(func: Callable) -> Callable: taskmgr.add_task(func) return func # color definition: # green: well-tested # cyan: should work # blue: not work and have to take fallback choice # magenta: unexpected or no fallback choice @new_task def check_platform(_: str): pcheck('system platform') if sys.platform == 'linux': print(TXT_GREEN('linux')) elif sys.platform == 'android': print(TXT_CYAN('android')) else: print(TXT_MAGENTA(sys.platform)) error(f'Ceccomp only support Linux!') @new_task def check_flock(makefile: Makefile): pcheck('if flock in system') if shutil.which('flock') is None: makefile.inject('VERBOSE', '1') print(TXT_BLUE('no')) warn('flock not found in system, Makefile verbose is set to true. You may need util-linux package') else: makefile.inject('VERBOSE', '1' if buildopts.verbose else '0') print(TXT_GREEN('yes')) @new_task def check_i18n(makefile: Makefile): pcheck('internationalization support') if not buildopts.i18n: makefile.inject('I18N', False) makefile.inject('LOCALEDIR', buildopts.localedir) makefile.inject('LOCALE_SED', 's|@@LOCALEDIR@@||') print(TXT_BLUE('no')) return if shutil.which('xgettext') is None: print(TXT_MAGENTA('no gettext')) error('xgettext and other tools not found, pass --without-i18n to disable l10n generation or install gettext package!') makefile.inject('I18N', True) makefile.inject('LOCALEDIR', buildopts.localedir) makefile.inject('LOCALE_SED', f's|@@LOCALEDIR@@|#define LOCALEDIR "{buildopts.localedir}"|') print(TXT_GREEN('yes')) @new_task def check_doc(makefile: Makefile): pcheck('documentation settings') if not buildopts.doc: makefile.inject('DOC', False) print(TXT_BLUE('no doc')) return if buildopts.asciidoc is None: print(TXT_MAGENTA('no asciidoc found')) error('Asciidoc compiler not found, pass --without-doc to disable documentation generation or install asciidoctor package!') makefile.inject('DOC', True) makefile.inject('ASCIIDOC', buildopts.asciidoc) if 'asciidoctor' in buildopts.asciidoc: print(TXT_GREEN(buildopts.asciidoc)) else: print(TXT_CYAN(buildopts.asciidoc)) warn(f'Your asciidoc compiler {buildopts.asciidoc} is not tested and may fail to generate docs') standard_c = ''' #include void leaf(void) {} int main() { puts(""); leaf(); return 0; } ''' @new_task def check_cc(makefile: Makefile): pcheck('C compiler') if buildopts.cc is None: print(TXT_MAGENTA('no cc found')) error('C compiler command not found in system!') code, _, stderr = run_command([buildopts.cc, '-x', 'c', '-', '-o', '/dev/null'], standard_c) if code: print(TXT_MAGENTA(buildopts.cc)) if code == -1: error(f'C compiler is not working: {stderr}') if code == -2: error(f'Unexpected error: {stderr}') print(f'Compiler returned non-zero returncode with message:\n{stderr}', end='') error(f'C compiler can not compile minimum C unit') # code == 0 makefile.inject('CC', buildopts.cc) print(TXT_GREEN(buildopts.cc)) linux_headers = ''' #include #include #include int main() { struct sock_fprog *addr = (struct sock_fprog *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); *addr = (struct sock_fprog){ .len = 0, .filter = NULL }; return 0; } ''' @new_task def check_linux_headers(_: Makefile): pcheck('Linux-related headers') code, _, stderr = run_command([buildopts.cc, '-x', 'c', '-', '-o', '/dev/null'], linux_headers, push_sys_dir=True) if code: print(TXT_MAGENTA('no')) if code == -1: error(f'C compiler is not working: {stderr}') if code == -2: error(f'Unexpected error: {stderr}') print(f'Compiler returned non-zero returncode with message:\n{stderr}', end='') error(f'Can not compile Linux-related unit, you may install linux-headers package') print(TXT_GREEN('yes')) libseccomp = ''' #include #include int main() { printf("%d", seccomp_version()->major); return 0; } ''' @new_task def check_libseccomp(_: Makefile): pcheck('libseccomp') code, _, stderr = run_command([buildopts.cc, '-x', 'c', '-', '-lseccomp', '-o', '/dev/null'], libseccomp, push_sys_dir=True) if code: print(TXT_MAGENTA('no')) if code == -1: error(f'C compiler is not working: {stderr}') if code == -2: error(f'Unexpected error: {stderr}') print(f'Compiler returned non-zero returncode with message:\n{stderr}', end='') error(f'Can not compile libseccomp unit, you may install libseccomp package') print(TXT_GREEN('yes')) argp = ''' #include int main(int argc, char **argv) { return argp_parse(0, argc, argv, 0, 0, 0); } ''' @new_task def check_argp(makefile: Makefile): pcheck('argp parser') code, _, stderr = run_command([buildopts.cc, '-x', 'c', '-', '-o', '/dev/null'], argp, push_sys_dir=True) if code: if code == -1: print(TXT_MAGENTA('no')) error(f'C compiler is not working: {stderr}') if code == -2: print(TXT_MAGENTA('no')) error(f'Unexpected error: {stderr}') # code > 0 # some system need external argp package, so we try to link code, _, stderr = run_command([buildopts.cc, '-x', 'c', '-', '-largp', '-o', '/dev/null'], argp) if code: print(TXT_MAGENTA('no')) if code == -1: print(TXT_MAGENTA('no')) error(f'C compiler is not working: {stderr}') if code == -2: print(TXT_MAGENTA('no')) error(f'Unexpected error: {stderr}') print(f'Compiler returned non-zero returncode with message:\n{stderr}', end='') error(f'Can not compile argp unit, you may install argp package') # code == 0 and link argp makefile.inject('LIBARGP', '-largp') print(TXT_CYAN('external')) return # code == 0 makefile.inject('LIBARGP', '') print(TXT_GREEN('builtin')) @new_task def check_source(_: Makefile): pcheck('source code type') if os.path.isdir('.git'): buildopts.git_install = True print(TXT_GREEN('git install')) else: buildopts.git_install = False print(TXT_CYAN('tarball install')) @new_task def check_version(makefile: Makefile): pcheck('target version') if buildopts.git_install: if not buildopts.devmode: code, stdout, stderr = run_command(['git', 'describe', '--exact-match']) if code < 0: print(TXT_MAGENTA('git failed')) print(f'Git not accessible? {stderr}') error('Can not run git, you may install git package') elif code == 0: # we are at a tag, adopt it tag = stdout.strip() buildopts.version = tag if not tag.startswith('v') else tag[1:] code, stdout, _ = run_command(['git', 'log', '-1', '--format=format:%as', tag]) if code: print(TXT_MAGENTA('get tag time failed')) print(stderr, end='' if stderr[-1] == '\n' else '\n') error('Can not get tag-time for the latest tag') buildopts.tag_time = stdout.strip() else: # we are not at a tag, try to go to a tag code, stdout, _ = run_command(['git', 'rev-list', '--tags', '--max-count=1']) if code: print(TXT_MAGENTA('git failed')) print(stderr, end='' if stderr[-1] == '\n' else '\n') error('Can not determine the latest tag by git, to build from git, you need to pull the whole repo!') code, stdout, _ = run_command(['git', 'describe', '--tags', stdout.strip()]) if code: print(TXT_MAGENTA('git failed')) print(stderr, end='' if stderr[-1] == '\n' else '\n') error('Can not determine the latest tag by git') tag = stdout.strip() code, stdout, _ = run_command(['git', 'checkout', tag]) # checkout to that tag if code: print(TXT_MAGENTA('checkout failed')) print(stderr, end='' if stderr[-1] == '\n' else '\n') error('Can not checkout the latest tag') print(TXT_CYAN(tag)) warn('Checked out to new tag, please rerun me to build stable version of ceccomp!') sys.exit(0) else: # devmode is True code, stdout, stderr = run_command(['git', 'describe', '--long']) if code < 0: print(TXT_MAGENTA('git failed')) print(f'Git not accessible? {stderr}') error('Can not run git, you may install git package') elif code == 0: # current commit get correctly resolved (tag-rev-gcommit or tag) # --long mode display in TAG-COMMITS-gCOMMIT tag, revision, commit = stdout.strip().split('-') ver = tag if not tag.startswith('v') else tag[1:] buildopts.version = f'{ver}.r{revision}_{commit[1:]}' # skip g code, stdout, _ = run_command(['git', 'log', '-1', '--format=format:%as']) if code: print(TXT_MAGENTA('get tag time failed')) print(stderr, end='' if stderr[-1] == '\n' else '\n') error('Can not get tag-time for the latest tag') buildopts.tag_time = stdout.strip() else: # git failed to describe print(TXT_MAGENTA('describe failed')) print(stderr, end='' if stderr[-1] == '\n' else '\n') error('Can not determine the latest tag by git, to build from git, you need to pull the whole repo!') else: # tarball install if not buildopts.version: buildopts.version = VERSION if not buildopts.tag_time: buildopts.tag_time = TAG_TIME makefile.inject('VERSION', buildopts.version) makefile.inject('TAG_TIME', buildopts.tag_time) if buildopts.git_install and not buildopts.devmode: print(TXT_GREEN(buildopts.version)) elif buildopts.git_install and buildopts.devmode: print(TXT_BLUE(buildopts.version)) else: print(TXT_CYAN(buildopts.version)) @new_task def check_builder_name(makefile: Makefile): pcheck('if builder name is valid') if '/' in buildopts.builder: error(f'Found \'/\' in builder name {buildopts.builder}, which is not allowed.') makefile.inject('BUILDER', buildopts.builder) print(TXT_GREEN('yes')) @new_task def check_omit_leaf_frame_pointer_flag(makefile: Makefile): pcheck('if compiler support -mno-omit-leaf-frame-pointer') code, _, _ = run_command([buildopts.cc, '-x', 'c', '-Werror', '-', '-mno-omit-leaf-frame-pointer', '-o', '/dev/null'], standard_c, push_sys_dir=True) if code: print(TXT_CYAN('no')) makefile.inject('ARCH_PRESERVE_FP', '') else: print(TXT_GREEN('yes')) makefile.inject('ARCH_PRESERVE_FP', '-mno-omit-leaf-frame-pointer') taskmgr.run_tasks() # vars not injected: EXTRA_CFLAGS EXTRA_LDFLAGS DEBUG_LEVEL PREFIX BINDIR ZSH_FPATH MANDIR DOCDIR DESTDIR SYS_INC_DIR SYS_LIB_DIR IS_STATIC taskmgr.makefile.inject('EXTRA_CFLAGS', buildopts.cflags) taskmgr.makefile.inject('EXTRA_LDFLAGS', buildopts.ldflags) taskmgr.makefile.inject('DEBUG_LEVEL', str(buildopts.debug_level)) taskmgr.makefile.inject('PREFIX', buildopts.prefix) taskmgr.makefile.inject('BINDIR', buildopts.bindir) taskmgr.makefile.inject('ZSH_FPATH', buildopts.zshfpath) taskmgr.makefile.inject('MANDIR', buildopts.mandir) taskmgr.makefile.inject('DOCDIR', buildopts.docdir) taskmgr.makefile.inject('DESTDIR', buildopts.destdir) taskmgr.makefile.inject('IS_STATIC', '1' if buildopts.is_static else '0') taskmgr.makefile.inject('SYS_INC_DIR', f'-I{buildopts.sys_includedir}' if buildopts.sys_includedir else '') taskmgr.makefile.inject('SYS_LIB_DIR', f'-L{buildopts.sys_libdir}' if buildopts.sys_libdir else '') assert taskmgr.makefile.s.find('{{') == -1 print('Writting back to Makefile... ', end='', flush=True) try: with open('Makefile', 'w') as f: f.write(taskmgr.makefile.s) except Exception as e: print(TXT_RED('failed')) error(f'Unexpected error when write back: {e}') else: print(TXT_GREEN('ok')) ceccomp-4.0/docs/000077500000000000000000000000001514205130000137055ustar00rootroot00000000000000ceccomp-4.0/docs/.gitignore000066400000000000000000000000051514205130000156700ustar00rootroot00000000000000*.mo ceccomp-4.0/docs/Makefile000066400000000000000000000001461514205130000153460ustar00rootroot00000000000000# NOTE: Don't edit translated adoc directly! They are automatically generated. update: po4a po4a.cfg ceccomp-4.0/docs/boot-slate.css000066400000000000000000000126511514205130000164750ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* Based on Slate from Bootswatch (https://bootswatch.com/slate/) */ /* This style sheet is distributed under MIT License */ /* document body (contains all content) */ body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.42857143; color: #c8c8c8; background-color: #272b30; margin-left: 10%; margin-right: 10%; } /* document header (contains title etc) */ #header { width: 100%; } #header>h1 { border-bottom: 1px solid #ddddd8; padding-bottom: 8px; } /* headings */ h1, h2, h3, h4, h5, h6 { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 500; line-height: 1.1; color: inherit; } h1, h2, h3 { margin-top: 20px; margin-bottom: 10px; } h4, h5, h6 { margin-top: 10px; margin-bottom: 10px; } h1 { font-size: 4em; /* font-size: 36px; */ } h2 { font-size: 30px; } h3 { font-size: 24px; } h4 { font-size: 18px; } h5 { font-size: 14px; } h6 { font-size: 12px; } /* plain paragraph text */ .paragraph { /* font-family: sans-serif; */ margin: 0 0 10px; } p { /* font-family: sans-serif; */ margin: 0 0 10px; } /* blockquote text */ .quoteblock { font-style: italic; } blockquote { padding: 10px 20px; margin: 0 0 20px; font-size: 17.5px; border-left: 5px solid #7a8288; } blockquote p:last-child, blockquote ul:last-child, blockquote ol:last-child { margin-bottom: 0; } .blockquote-reverse, blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #7a8288; border-left: 0; text-align: right; } .blockquote-reverse footer:before, blockquote.pull-right footer:before, .blockquote-reverse small:before, blockquote.pull-right small:before, .blockquote-reverse .small:before, blockquote.pull-right .small:before { content: ''; } .blockquote-reverse footer:after, blockquote.pull-right footer:after, .blockquote-reverse small:after, blockquote.pull-right small:after, .blockquote-reverse .small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } /* blockquote attribution text */ .attribution, .cite, blockquote footer, blockquote small, blockquote .small { display: block; line-height: 1.42857143; color: #7a8288; } .attribution:before, blockquote footer:before, blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } /* unordered list */ ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } /* links */ a { color: #ffffff; text-decoration: none; } a:hover, a:focus { color: #ffffff; text-decoration: underline; } a:focus { outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } /* horizontal rules */ hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #1c1e22; } /* table */ table { background-color: transparent; width: 100%; max-width: 100%; margin-bottom: 21px; border-collapse: collapse; } table col[class*="col-"] { position: static; float: none; display: table-column; } table td[class*="col-"], table th[class*="col-"] { position: static; float: none; display: table-cell; } /* table caption */ caption { padding-top: 8px; padding-bottom: 8px; color: #7a8288; text-align: left; } /* table header row */ thead { border-bottom: 2px solid #1c1e22; } /* table header cell */ th { text-align: left; padding-left: 8px; } /* table footer */ tfoot { color: #807F81; border-top: 1px solid #1c1e22; } /* table cell */ td { border-top: 1px solid #1c1e22; } td p { margin: auto; padding: 8px; } /* table body */ tbody > tr:nth-of-type(odd) { background-color: #353a41; } tbody > tr:hover { background-color: #49515a; } /* inline code */ code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } code { padding: 2px 4px; font-size: 90%; color: #e3c7d2; background-color: #444951; border-radius: 4px; } kbd { padding: 2px 4px; font-size: 90%; color: #ffffff; background-color: #333333; border-radius: 3px; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); } kbd kbd { padding: 0; font-size: 100%; font-weight: bold; -webkit-box-shadow: none; box-shadow: none; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.42857143; word-break: break-all; word-wrap: break-word; color: #dcc1cc; background-color: #33373d; border: 1px solid #cccccc; border-radius: 4px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } /* image */ img { max-width: 100%; vertical-align: middle; } /* footer section */ #footer { margin-top: 22px; padding: 14px 16px; background-color: #3a3f44; } /* responsiveness fixes */ video { max-width: 100%; } /* table of Contents sidebar */ #toctitle { color: #272b30; } #toc ul { display: inline; list-style-type: none; margin: 0; padding: 0; overflow: hidden; } #toc li { display: block; } #toc a { background-color: #3a3f44; float: left; text-align: center; padding: 14px 16px; text-decoration: none; } #toc li a:hover { background-color: #272b2e; text-decoration: none; } #toc:after { content: " "; visibility: hidden; display: block; height: 0; clear: both; } @media all and (max-width: 600px) { table { width: 55vw!important; font-size: 3vw; } } ceccomp-4.0/docs/ceccomp.adoc000066400000000000000000000404451514205130000161550ustar00rootroot00000000000000= ceccomp(1) dbgbgtf ; RocketDev :doctype: manpage :docdatetime: {TAG_TIME} :manmanual: Ceccomp Manual :mansource: ceccomp {VERSION} :manversion: {VERSION} :imagesdir: images/ :stylesheet: boot-slate.css == NAME ceccomp - A tool to analyze seccomp filters == SYNOPSIS usage: ceccomp [FILE] [-q|--quiet] [-f|--format FMT] [-a|--arch ARCH] [-p|--pid PID] [-s|--seize] [-o|--output FILE] [-c|--color WHEN] ... == CONCEPT Kernel use BPF filters to limit syscall rules, applied via `seccomp` or `prctl` syscall. For example, down below is a simple filter to block `execve` syscall in hex format: 1: 20 00 00 00 00 00 00 00 $A = $syscall_nr 2: 15 00 00 01 3b 00 00 00 if ($A != execve) goto 4 3: 06 00 00 00 00 00 00 00 return KILL 4: 06 00 00 00 00 00 ff 7f return ALLOW The part presented in hex is what kernel received, and `ceccomp` take it to disassemble back to human readable text. For instance the *lineno* in the left and *statement* in the right. IMPORTANT: Later I'll use _TEXT_ in short for BPF human readable text, and use _RAW_ in short for BPF raw format, please keep that in mind. == DESCRIPTION `ceccomp` have 5 main functions, basically it's a C version of `seccomp-tools`, however, there are some breaking changes you need to know, which will be highlighted in each subcommand section. === asm - ASSEMBLE ceccomp asm [-c WHEN] [-a ARCH] [-f FMT] [TEXT] Assemble _TEXT_ to _RAW_. Use it to embed hand written filter rules into C code or to see the original code of some _TEXT_. WHEN:: Determines when to display warnings and errors in color. If the value is _auto_, ceccomp will display color when the output target is a "tty". Can be _auto_, _never_ or _always_. The default value is _auto_. ARCH:: Set to any architecture libseccomp supports. Will be used to determine the actual syscall number behind the name (for example, on x86_64, you could write `"execve"` instead of `59` like the basic example above). Your system arch will be taken if not set via `uname`. The default value on your system is {ARCH}. NOTE: Since _version 4.0_, endianness is considered. If target endianness *ARCH* is different from machine endianness, the filters will be reversed (CODE and K) before outputting. FMT:: Determines how `ceccomp` produces binary-format bpf code. Can be _hexfmt_, _hexline_ or _raw_. You could find sample output in <> section. The default value is _hexline_. TEXT:: Take a optional filename to determine which file containing _TEXT_ will be assembled. Will read from _stdin_ if not set. `-` is treated as _stdin_. IMPORTANT: The assembly syntax was changed greatly since _version 4.0_, please checkout grammar reference below! Please check out <> section to see how to write a rule by hand. Some examples will be displayed in <> section. |=== |Command|Difference |`seccomp-tools asm` |Use its own grammar to assemble, a bit script like; can assemble invalid _TEXT_ which will be rejected by kernel |`ceccomp asm` |You can just take `disasm` output to `asm`, no new grammar is needed to learn; take `stdin` as input by default |=== === disasm - DISASSEMBLE ceccomp disasm [-c WHEN] [-a ARCH] [RAW] Disassemble _RAW_ to _TEXT_. Use it to see what does a filter do if you could not access filter via `trace` and have to manually extract the filter out. WHEN:: Argument description can be found in <> section. `disasm` may print more text in color including syntax highlighting for _TEXT_. ARCH:: Set to any architecture libseccomp supports. Will be used to determine how filtered syscall number in _RAW_ filter is translated to syscall name (for example, on x86_64, the number `0x3b` is translated to `execve` if is comparing syscall_nr, see the basic example above). The default value on your system is {ARCH}. RAW:: A binary file with raw BPF codes. Takes _stdin_ as input if not set. Treat `-` as _stdin_. The file is arch-revelent, so it may not be portable on different archs. NOTE: Since _version 4.0_, endianness is considered. If target endianness *ARCH* is different from machine endianness, the filters will be reversed (CODE and K) before decoding. NOTE: ceccomp will try to resolve syscall number under an arch ONLY IF that at that line, arch can be determined. On foreign arch (not equal to the arch you set), the foreign arch will be prepended to syscall name. You may notice that in some cases, seccomp-tools is able to resolve the name while ceccomp is not, that may be intended as the arch is not determined. |=== |Command|Difference |`seccomp-tools disasm` |Disassembles in its format; never check if the filter is valid |`ceccomp disasm` |Disassembles in ceccomp format, and takes `stdin` as input by default; check arch strictly and always display foreign arch name |=== === emu - EMULATE ceccomp emu [-c WHEN] [-a ARCH] [-q] TEXT SYSCALL_NAME/SYSCALL_NR [ARGS[0] ARGS[1] ... ARGS[5] PC] Emulate what will happen if `syscall(SYSCALL_NR, ARGS[0], ARGS[1], ..., ARGS[5])` from `PC` is called following rules described in _TEXT_. Use it to see the result without actually running it in program or you don't want to examine the filter rule manually. This subcommand can be used to automatically examining a filter. WHEN:: Argument description can be found in <> section. `emu` may print more text in color including syntax highlighting for _TEXT_ and skipped statements. SYSCALL_NAME/SYSCALL_NR:: If you set *SYSCALL_NAME* (like `execve`), it will be translated to *SYSCALL_NR* under *ARCH* first. Or else set *SYSCALL_NR* directly (like `59`). Then the nr will be tested against the bpf filter to see the result of that syscall. This argument is NOT optional. ARGS[0-5] and PC:: Register values when calling syscall. For example, on x86_64, these are equivalent to `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9` and `rip`. Their default value is 0. ARCH:: Argument description can be found in <> section. TEXT:: Take a filename to determine which file containing _TEXT_ rule will be tested. Note that filename CAN NOT be ignored as ceccomp can not determine if a positional argument is syscall or filename. Use `-` to refer to _stdin_. -q, --quiet:: Only print the eval result of the filter. For example, if last statement emulated is `return KILL`, then `KILL` is printed. |=== |Command|Difference |`seccomp-tools emu` |Take a _RAW_ as input |`ceccomp emu` |Take a _TEXT_ as input and take `stdin` as input by default; set *PC* is possible |=== === trace - TRACE FILTER IN RUNTIME ceccomp trace [-c WHEN] [-q] [-o FILE] PROGRAM [program-args] [-c WHEN] [-q] -p PID [-s] The first line captures filters *PROGRAM* loads in runtime by tracing it; the second line extract seccomp filters from *PID*, or trace *PID* to capture subsequent seccomp filters; once fetched filters, print them in _TEXT_. You can only choose one of the two formats above. Use this if running the program is the simplest way to fetch bpf filters or a program with seccomp filters installed is waiting for input. WHEN:: Argument description can be found in <> section. `trace` may print more text in color including syntax highlighting for _TEXT_. FILE:: May be useful when *PROGRAM* produces quite a lot output in _stderr_. `ceccomp` allow user to close _stdin_ and _stdout_ to limit *PROGRAM* input and output, so `ceccomp` use _stderr_ to print messages when running *PROGRAM*, set *FILE* if you want to see _TEXT_ in some other file. Treat `-` as _stdout_. PROGRAM:: Set to the program you want to run, and *program-args* are its arguments just like running shell command `exec PROGRAM program-args`. PID:: Set to the pid you want to inspect. *PID* is conflict with *PROGRAM*; you could either run a program dynamically or examine a pid in one command. Without `-s` flag, trace pid will try to extract seccomp filter in *PID* via `ptrace(PTRACE_SECCOMP_GET_FILTER)`, which may not be available in some systems. -s, --seize:: *ONLY AVAILABLE FOR TRACE PID MODE.* Set this flag will override trace pid behavior to attach to *PID* and keep tracing for seccomp filter loading like trace prog mode. _This flag was introduced in version 4.0._ -q, --quiet:: Set to suppress the extra *[INFO]* prints when detect process forking, exiting or seccomp filter loading. _This flag was introduced in version 4.0._ NOTE: To extract filters from *PID*, `CAP_SYS_ADMIN` is needed (without `-s` flag) and `CAP_SYS_PTRACE` may also be needed, the easiest way to acquire them is calling `ceccomp` with `sudo`. NOTE: Since _version 3.1_, multiple process tracing is introduced, and when tracee forking/resolving/exiting, an extra INFO message is printed. You can discard it by running command like `ceccomp trace -q PROG 2>/dev/null`. |=== |Command|Difference |`seccomp-tools dump` |Setting output format is possible; each filter can be output to a different file; killing *PROGRAM* once *LIMIT* times of filters loaded; wrapping *PROGRAM* in `sh -c` |`ceccomp trace` |All filters are output to a single file; never kill *PROGRAM*; *PROGRAM* is launched directly, so `./` is not needed; explicitly print when forking; able to attach to pid for dynamic seccomp filter capturing |=== === probe - TEST COMMON SYSCALLS INSTANTLY ceccomp probe [-c WHEN] [-o FILE] [-q] PROGRAM [program-args] Run *PROGRAM* with *program-args* to captures *FIRST* seccomp filter, and then kill all children. Use it when a quick check against a program is needed, and detect potential seccomp rule issues. All argument descriptions can be found in <> section. The output for this subcommand is the emulating result of common syscalls like `execve`, `open` and so on. If the filter itself is not capable of blocking syscalls, you could know that with a glance. Typical output for this subcommand is described below, more detailed example could be found in <> section. open -> ALLOW read -> ALLOW write -> ALLOW execve -> KILL execveat -> KILL mmap -> ALLOW mprotect -> ALLOW openat -> ALLOW sendfile -> ALLOW ptrace -> ERRNO(1) fork -> ALLOW NOTE: `seccomp-tools` don't have this subcommand. == TEXT GRAMMAR REFERENCE IMPORTANT: The grammar changed greatly since _version 4.0_ as we refactored lexer for better human readability. The wrapper now prefixed by `#` as it's a comment now. And _line_ is replaced by _label_, so now lexer depends on label declaration to decide where to jump, instead of lineno in _TEXT_ file. A valid _TEXT_ format is described in EBNF-like declaration here: https://github.com/dbgbgtf1/Ceccomp/issues/17#issuecomment-3610531705. If you have no interest to know what EBNF is, please keep reading for examples. BPF ops which are not described below are banned by kernel. === Comment and Label `ceccomp disasm` displays a lot of things, but some of them are optional for asm. #Label CODE JT JF K #--------------------------------- L0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- Any text after `#` will be discarded by asm like some script languages. Empty lines are accepted. Label declaration is an identifier at the beginning of line and suffixed by `:` like `L0001`. An identifier is a string starts with alpha and contains with only alphanumeric characters and underscore `_`. Label is only necessary if it's the destination of `goto`, these redundant labels added by disasm are for readability. E.g. in `if ($A == 0) goto somewhere`, `somewhere` is a label and must be declared after the statement. Label declaration can take a line separately, or be put in front of statement. The `CODE`, `JT`, `JF` and `K` value generated by disasm will be discarded by asm, asm only parse the effective statement after `K`. NOTE: There are some slight difference between `ceccomp disasm` and `seccomp-tools disasm`, down below is a general example. And some statements are different, so don't pipe seccomp-tools output to ceccomp blindly. line CODE JT JF K ================================= 0000: 0x06 0x00 0x00 0x7fff0000 return ALLOW === Assignment `A` can be set to seccomp attributes directly. But `X` can not be assigned with seccomp attributes directly due to kernel limit. $A = $arch $A = $syscall_nr To assign `A` with those 64-bit long fields, `low_` or `high_` prefix is needed. $A = $low_pc $A = $high_pc $A = $low_args[0] $A = $high_args[0] ... $A = $low_args[5] $A = $high_args[5] A special attribute is `sizeof(struct seccomp_data)`, that can be assigned to `A` or `X` directly. $A = $scmp_data_len $X = $scmp_data_len Temporary memory is 32-bit, to access them, you could use hex or dec as index. Both `A` and `X` is assignable. Assigning immediate values to `A` or `X` accepts any format of number if you imply the correct base by "0x" or "0b". $X = $mem[0] $A = $mem[0xf] $A = $mem[15] # both hex and dec index are OK $A = 0 $X = 0x3b $A = 0b1111 $A = 0333 You could also assign `X` to `A` or in the reverse order. Assign `X` or `A` to temporary memory is definitely okay. $A = $X $X = $A $mem[3] = $X $mem[0x4] = $A === Arithmetic Operations Various operations can be applied to `A`. $A += 30 $A -= 4 $A *= 9 $A /= 1 $A &= 7 $A >>= 6 The right value can be `X`. $A &= $X $A |= $X $A ^= $X $A <<= $X And there is a way to negativate `A`. $A = -$A === Jump Downwards If ... Unconditional jump: goto L3 Jump if: if ($A == execve) goto L3 if ($A != 1234) goto L4 if ($A & $X) goto L5 if !($A & 7) goto L6 if ($A <= $X) goto L7 If true jump to ... if false jump to...: if ($A > $X) goto L3, else goto L4 if ($A >= 4567) goto L5, else goto L6 ONLY in conditions, you CAN replace number with syscall name or arch name. In example above, `0x3b` is replaced by `execve`. All the syscall name will be resolved to syscall number under your selected arch. If you want to resolve a syscall name in foreign arch (not equal to your selected arch), please prepend a arch and dot. For example, your arch is x86_64, and you are writing _aarch64_ rules, then please write like: if ($A == aarch64.read) goto 5 Note that if you manually set arch to _aarch64_ with `-a aarch64`, you can omit `aarch64.` in statement. === Return Code Return value of register `A`: return $A Or return a immediate value, with extra field in `()`. Actions including `TRACE`, `TRAP` and `ERRNO` accept an extra field, without `()`, they are treated as `action(0)`. return 0x13371337 return KILL return KILL_PROCESS return TRAP(123) return ERRNO(0) return TRACE return TRACE(3) return LOG return NOTIFY === Short Example The following _TEXT_ is valid for asm, which blocks `execve` and `execveat` for amd64 syscalls: $A = $syscall_nr if ($A == execve) goto forbid if ($A == execveat) goto forbid return ALLOW forbid: return KILL == RESTRICTIONS Ceccomp asm put some restrictions on _TEXT_ for better performance. 1. `'\0'` must not be found in _TEXT_ since it's a text file. 2. A line must be shorter than 384 *bytes*. 3. A _TEXT_ file must have less than 4096 lines. 4. A _TEXT_ file must be smaller than 1 MiB. And for both asm and disasm, effective statements (that can be encoded or decoded into BPF) must be less or equal than 1024, this is enforced by kernel. A fun fact about ceccomp asm: any basic ANSI color in _TEXT_ file, e.g., `\x1b[31m`, will be discarded when processing. == EXAMPLES ifdef::backend-manpage[] Manpage can not display images, so please check out html version of this page to see examples. endif::[] ifndef::backend-manpage[] === asm example image::asm.webp[] === disasm example image::disasm.webp[] === emu example image::emu.webp[] === trace example Running program: image::trace.webp[] If set `-o FILE`: image::output_trick.webp[] Trace pid mode: image::trace_pid.webp[] Seize pid mode: image::trace_seize.webp[] Completion for pid mode is available under zsh: image::trace_completion.webp[] === probe example image::probe.webp[] endif::[] == REPO Visit https://github.com/dbgbgtf1/Ceccomp to find the code. Pull Requests and Issues are welcome! Copyright (C) 2025-present, distributed under GPLv3 or later. ceccomp-4.0/docs/ceccomp.zh_CN.adoc000066400000000000000000000414571514205130000171610ustar00rootroot00000000000000= ceccomp(1) dbgbgtf ; RocketDev :doctype: manpage :docdatetime: {TAG_TIME} :manmanual: Ceccomp Manual :mansource: ceccomp {VERSION} :manversion: {VERSION} :imagesdir: images/ :stylesheet: boot-slate.css == 名称 ceccomp - 一个分析安全计算 (seccomp) 过滤器的工具 == 大纲 usage: ceccomp [FILE] [-q|--quiet] [-f|--format FMT] [-a|--arch ARCH] [-p|--pid PID] [-s|--seize] [-o|--output FILE] [-c|--color WHEN] ... == 概念 内核使用BPF过滤器来限制系统调用规则,并使用 `seccomp` 和 `prctl` 两个系统调用来安装过滤器。以下是一个以十六进制表示的限制 `execve` 系统调用的简单过滤器: 1: 20 00 00 00 00 00 00 00 $A = $syscall_nr 2: 15 00 00 01 3b 00 00 00 if ($A != execve) goto 4 3: 06 00 00 00 00 00 00 00 return KILL 4: 06 00 00 00 00 00 ff 7f return ALLOW 以上十六进制的部分就是内核收到的过滤器,而 `ceccomp` 负责把它拿来反汇编为人类可读的文本。 例如左侧的 *行号* 和右侧的 *伪代码* 。 IMPORTANT: 之后我会使用 _TEXT_ 作为BPF过滤器人类可读文本(伪代码)的缩写, 使用 _RAW_ 作为BPF过滤器原始格式的缩写,请记住这个约定。 == 描述 `ceccomp` 有5个主要的功能,它基本上是C版本的 `seccomp-tools` ,然而, 有一些不同的地方你需要知道,它们会在在每个子命令的章节中被注明。 === asm - 汇编 ceccomp asm [-c WHEN] [-a ARCH] [-f FMT] [TEXT] 将 _TEXT_ 汇编为 _RAW_ 。适用于将手写的过滤器规则嵌入到C代码中, 或希望观察一些 _TEXT_ 对应的原始字节码。 WHEN:: 决定了 `ceccomp` 何时输出有颜色的文本。当设置为 _auto_ 时, `ceccomp` 会在输出目标是一个“tty”时打印颜色。可以是 _auto_ 、 _never_ 或者 _always_ 。 默认值是 _auto_ 。 ARCH:: 可以设置为任何 libseccomp 支持的架构。它将被用于决定系统调用名称对应的系统调用号。 例如,在 x86_64 上,就像上面的基本示例,你可以写 `"execve"` 而不是数字 `59` 指代系统调用号。如果不设置这个参数,则通过 `uname` 提取当前系统的架构。 你的系统上的默认值是 {ARCH} 。 NOTE: 从 _4.0 版本_ 开始考虑端序。如果目标架构 *ARCH* 的端序与机器端序不同,则会在输出前反转过滤器(CODE 和 K)。 FMT:: 决定了 `ceccomp` 如何输出二进制格式的BPF字节码。可以是 _hexfmt_ 、 _hexline_ 或者 _raw_ 。你可以在 <> 节中找到示例输出。默认值是 _hexline_ 。 TEXT:: 一个可选的文件名,其中存放了需要被汇编的 _TEXT_ 。不设置则从 _标准输入_ 中读取。`-` 被视作 _标准输入_。 IMPORTANT: _4.0 版本_ 大幅修改了汇编的语法,请查阅下面的语法参考! 查看 <> 一节可以找到如何手写规则。一些示例会在 <> 一节中展示。 |=== |命令|差别 |`seccomp-tools asm` |使用它自己的语法汇编,有点像脚本;可以汇编会被内核拒绝的错误 _TEXT_ |`ceccomp asm` |你可以直接拿着 `disasm` 的输出来汇编,不需要学习新语法;默认使用 _标准输入_ 作为输入 |=== === disasm - 反汇编 ceccomp disasm [-c WHEN] [-a ARCH] [RAW] 反汇编 _RAW_ 为 _TEXT_ 。适用于当你无法使用 `trace` 看到过滤器时,必须手动提取过滤器, 然后检查其含义。 WHEN:: 参数描述可以在 <> 一节中找到。 `disasm` 可能会打印更多有颜色的文本,包括针对 _TEXT_ 的语法高亮。 ARCH:: 可以设置为任何 libseccomp 支持的架构。它将被用于决定 _RAW_ 中的系统调用号如何被翻译为系统调用名。 例如,在 x86_64 上,在比较系统调用号时,数字 `0x3b` 将被翻译为 `execve` ,可以看上面的基本示例。 你的系统上的默认值是 {ARCH} 。 RAW:: 包含原始 BPF 代码的二进制文件。如果不设置则将 _标准输入_ 作为输入。`-` 被视作 _标准输入_。这个文件是与架构有关的,所以它在不同架构之间可能不是通用的。 NOTE: 从 _4.0 版本_ 开始考虑端序。如果目标架构 *ARCH* 的端序与机器端序不同,则会在解码前反转过滤器(CODE 和 K)。 NOTE: 当且仅当在某一行的架构可以被确定时,ceccomp 才会尝试用那个架构解析那个系统调用号。 如果是外部架构(不等于你设置的架构),它会被附加到系统调用名前。你可能会注意到在一些情况下, seccomp-tools 能解析一些系统调用名,而 ceccomp 不能,这可能是因为此时架构不能被确定。 |=== |命令|差别 |`seccomp-tools disasm` |用它自己的语法反汇编;永远不会检查 _RAW_ 是否合法 |`ceccomp disasm` |用 ceccomp 语法反汇编,并且默认将 _标准输入_ 作为输入;严格检查架构, 并且永远打印外部架构名 |=== === emu - 模拟 ceccomp emu [-c WHEN] [-a ARCH] [-q] TEXT SYSCALL_NAME/SYSCALL_NR [ARGS[0] ARGS[1] ... ARGS[5] PC] 按照 _TEXT_ 中描述的规则,模拟从 `PC` 调用 `syscall(SYSCALL_NR, ARGS[0], ARGS[1], ..., ARGS[5])` 的结果。适用于在不实际运行程序或不想手动检查规则时,查看触发系统调用的结果。 这个子命令适合用来自动检测一个过滤器。 WHEN:: 参数描述可以在 <> 一节中找到。 `emu` 可能会打印更多有颜色的文本,包括针对 _TEXT_ 的语法高亮以及跳过的伪代码。 SYSCALL_NAME/SYSCALL_NR:: 如果你设置了 *SYSCALL_NAME* (比如 `execve` ),那么它会基于 *ARCH* 先被翻译为对应的 *SYSCALL_NR* 。或者你可以直接设置 *SYSCALL_NR* (例如 `59` )。然后会测试这个系统调用号经过 BPF 过滤器处理后的输出并打印出来。这个参数是必填的。 ARGS[0-5] 和 PC:: 当调用系统调用时对应寄存器的值。例如,在 x86_64 上,它们分别对应了 `rdi` 、 `rsi` 、 `rdx` 、 `r10` 、 `r8` 、 `r9` 和 `rip` 。它们的默认值都是0。 ARCH:: 参数描述可以在 <> 一节中找到。 TEXT:: 一个文件名,其中存放了需要被测试的 _TEXT_ 规则。注意文件名 不能 被忽略,因为 ceccomp 无法判断一个位置参数是系统调用号还是文件名。使用 `-` 可以指代 _标准输入_。 -q, --quiet:: 只打印过滤器的模拟结果。例如,最后一行模拟的伪代码是 `return KILL`,那么将会打印 `KILL`。 |=== |命令|差别 |`seccomp-tools emu` |用 _RAW_ 作为输入 |`ceccomp emu` |用 _TEXT_ 作为输入,并默认将 _标准输入_ 作为输入;可以设置 *PC* |=== === trace - 运行时捕获过滤器 ceccomp trace [-c WHEN] [-o FILE] PROGRAM [program-args] [-c WHEN] -p PID [-s] 使用第一行的命令可以利用调试在 *PROGRAM* 运行中加载过滤器时动态捕获过滤器; 第二行的命令可以从 *PID* 对应的进程中提取出 seccomp 过滤器,或通过调试 *PID* 捕获后续的 seccomp 过滤器;一旦捕获到了过滤器, 将会以 _TEXT_ 的格式将它打印出来。你可以从两个格式中选择一个使用。 适用于运行一个程序是捕获BPF过滤器最简单的方式或者一个安装了 seccomp 过滤器的程序正在等待输入。 WHEN:: 参数描述可以在 <> 一节中找到。 `trace` 可能会打印更多有颜色的文本,包括针对 _TEXT_ 的语法高亮。 FILE:: 当 *PROGRAM* 会产生很多输出到 _标准错误_ 时可能很有用。 `ceccomp` 允许用户关闭 _标准输入_ 和 _标准输出_ 来限制 *PROGRAM* 的输入和输出,所以 当运行 *PROGRAM* 时 `ceccomp` 使用 _标准错误_ 来打印消息。如果你想在别的文件中看见 _TEXT_ 的话请设置 *FILE* 。`-` 被视作 _标准输出_。 PROGRAM:: 设置为你想运行的程序,并且 *program-args* 将作为它的参数, 就像运行 shell 命令 `exec PROGRAM program-args` 。 PID:: 设置为你想检查的 pid。 *PID* 和 *PROGRAM* 相冲突;你只能在一条命令中动态运行一个程序, 或者检查一个 pid。没有 `-s` 标志,trace pid 会尝试使用 `ptrace(PTRACE_SECCOMP_GET_FILTER)` 从 *PID* 中提取 seccomp 过滤器,这个操作在一些系统上可能不可用。 -s, --seize:: *只适用于 TRACE PID 模式。* 设置这个标志将会覆盖 trace pid 的行为为像 trace prog 模式一样把调试器挂到 *PID* 上并持续跟踪 seccomp 过滤器的加载。_这个标志引入于 4.0 版本。_ -q, --quiet:: 设置这个标志将会在检测到进程分叉、退出或加载 seccomp 过滤器时抑制多余的 *[INFO]* 输出。 _这个标志引入于 4.0 版本。_ NOTE: 要想从 *PID* 中提取过滤器,你需要 `CAP_SYS_ADMIN` (没有 `-s` 标志),同时还可能需要 `CAP_SYS_PTRACE` ,最简单的获取它们的方法是用 `sudo` 运行 `ceccomp` 。 NOTE: 从 _3.1 版本_ 开始引入了多进程支持,并且当被调试进程 fork/resolve/exit 时,将会打印一条额外的 INFO 信息。你可以使用像 `ceccomp trace -q PROG 2>/dev/null` 这样的命令丢弃它。 |=== |命令|差别 |`seccomp-tools dump` |可以设置输出格式;每一个过滤器可以输出到不同的文件;当 *PROGRAM* 加载了 *LIMIT* 个过滤器后就杀死程序;将 *PROGRAM* 包装在 `sh -c` 中运行 |`ceccomp trace` |所有过滤器被输出到同一个文件;永远不会杀死 *PROGRAM* ; *PROGRAM* 是直接被执行的, 所以不需要 `./` ;当 fork 时,显式打印事件;能够附加调试器到 pid 上动态捕捉 seccomp 过滤器 |=== === probe - 快速测试常见的系统调用 ceccomp probe [-c WHEN] [-o FILE] [-q] PROGRAM [program-args] 以 *program-args* 为参数运行 *PROGRAM* 来捕获 *第一个* seccomp 过滤器, 然后杀死所有子进程。适用于快速测试一个程序的规则并检测潜在的 seccomp 规则问题。 所有参数描述都可以在 <> 一节中找到。 这个子命令的输出是一系列常见的系统调用的模拟结果,例如 `execve` 、 `open` 等。 如果过滤器本身并不能阻拦系统调用,那你一眼就能看出来。 这个子命令的典型输出如下所示,更多完整的实例可以在 <> 一节中找到。 open -> ALLOW read -> ALLOW write -> ALLOW execve -> KILL execveat -> KILL mmap -> ALLOW mprotect -> ALLOW openat -> ALLOW sendfile -> ALLOW ptrace -> ERRNO(1) fork -> ALLOW NOTE: `seccomp-tools` 没有等价的子命令。 == TEXT 语法参考 IMPORTANT: _4.0 版本_ 提高了词法分析器代码的可读性,伴随着大幅修改的语法。前缀为 `#` 的行现在是注释了。同时 _行号_ 被替换为 _标签_,现在词法分析器根据标签定义来决定跳转到哪里,而不是根据 _TEXT_ 文件中的行号。 这里有正确的 _TEXT_ 格式,以类 EBNF 语法描述:https://github.com/dbgbgtf1/Ceccomp/issues/17#issuecomment-3610531705。 没兴趣了解 EBNF?请继续阅读以下的例子。 其余未描述到的BPF操作都被内核禁止了。 === 注释与标签 `ceccomp disasm` 展示了很多东西,但对于 asm 来说有些是可选的。 #Label CODE JT JF K #--------------------------------- L0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- 任何在 `#` 之后的文本将被 asm 丢弃,就像一些脚本语言一样。 允许空行。 标签声明是一个从行首开始并以 `:` 结尾的标识符,例如 `L0001`。标识符是一个以字母为首,中间只包含字母、数字和下划线 `_` 的字符串。标签只在它是 `goto` 的目标时才是必要的,由 disasm 添加的多余的标签知识为了可读性。例如在 `if ($A == 0) goto somewhere` 中,`somewhere` 是一个标签并且必须在这行伪代码后声明。标签声明可以单独占据一行,也可以放置在伪代码的前面。 由 disasm 生成的 `CODE`、`JT`、`JF` 和 `K` 值会被 asm 丢弃,asm 之解析 `K` 之后的有效伪代码。 NOTE: `ceccomp disasm` 和 `seccomp-tools disasm` 的输出之间有很多细微的差别, 以下是一个典型的输出示例。同时有些伪代码是不同的,所以不要盲目将 seccomp-tools 的输出管道给 ceccomp。 line CODE JT JF K ================================= 0000: 0x06 0x00 0x00 0x7fff0000 return ALLOW === 赋值 `A` 可以直接赋值为 seccomp 属性。由于内核限制, `X` 不能直接赋值为 seccomp 属性。 $A = $arch $A = $syscall_nr 要给 `A` 赋值为这些64位长的字段,必须使用 `low_` 或者 `high_` 的前缀。 $A = $low_pc $A = $high_pc $A = $low_args[0] $A = $high_args[0] ... $A = $low_args[5] $A = $high_args[5] 一个特殊的属性是 `sizeof(struct seccomp_data)` ,它可以直接赋值给 `A` 或 `X` 。 $A = $scmp_data_len $X = $scmp_data_len 临时内存是32位的,要想访问它们,你可以使用十六进制或者十进制的索引。 `A` 和 `X` 都是可赋值的。给 `A` 或 `X` 赋值为立即数接受任意格式的数字,只要你使用 "0x" 或者 "0b" 等前缀正确表达数字是几进制的。 $X = $mem[0] $A = $mem[0xf] $A = $mem[15] # both hex and dec index are OK $A = 0 $X = 0x3b $A = 0b1111 $A = 0333 你还可以将 `X` 赋值给 `A` 或者反过来。将 `X` 或 `A` 赋值给临时内存当然可以。 $A = $X $X = $A $mem[3] = $X $mem[0x4] = $A === 数学运算 你可以以多种方式操作 `A` 。 $A += 30 $A -= 4 $A *= 9 $A /= 1 $A &= 7 $A >>= 6 右值也可以是 `X` 。 $A &= $X $A |= $X $A ^= $X $A <<= $X 想要对 `A` 取反可以这么做。 $A = -$A === 当...时向下跳转 无条件跳转: goto L3 当...跳转: if ($A == execve) goto L3 if ($A != 1234) goto L4 if ($A & $X) goto L5 if !($A & 7) goto L6 if ($A <= $X) goto L7 当条件为真时跳转到...,条件为假时跳转到...: if ($A > $X) goto L3, else goto L4 if ($A >= 4567) goto L5, else goto L6 *只有* 在做条件判断时,你才能将数字替换为系统调用号或架构名。在以上的例子中, `0x3b` 被 `execve` 替换。所有系统调用名将会以你设置的架构解析为系统调用号。 如果你希望解析外部架构(不等于你设置的架构)的系统调用名, 请在前面附加架构名和一个点。例如,你设置的架构是 x86_64,并且你正在写 _aarch64_ 架构的规则,请这样写: if ($A == aarch64.read) goto 5 注意当你手动使用 `-a aarch64` 将架构设置为 _aarch64_ 时, 你可以在伪代码中忽略 `aarch64.` 。 === 返回码 返回寄存器 `A` 的值: return $A 或者返回一个立即数,多余的字段放在 `()` 里。 `TRACE` 、 `TRAP` 和 `ERRNO` 接受一个额外的字段,如果没有 `()` ,它们将被视为 `行为(0)` 。 return 0x13371337 return KILL return KILL_PROCESS return TRAP(123) return ERRNO(0) return TRACE return TRACE(3) return LOG return NOTIFY === 简短的例子 下面的 _TEXT_ 对与 asm 来说是正确的,这段 _TEXT_ 阻止了 amd64 的 `execve` 和 `execveat` 系统调用: $A = $syscall_nr if ($A == execve) goto forbid if ($A == execveat) goto forbid return ALLOW forbid: return KILL == 限制 为了更好的性能,Ceccomp asm 对 _TEXT_ 有一些限制。 1. 由于 _TEXT_ 是个文本文件,`'\0'` 不能出现在 _TEXT_ 中。 2. 一行必须短于 384 *字节*。 3. 一个 _TEXT_ 文件必须短于 4096 行。 4. 一个 _TEXT_ 文家必须小于 1 MiB。 并且对于 asm 和 disasm 来说,有效的伪代码(能被编码或解码为 BPF 的)必须少于等于 1024 条,这是内核规定的。 一个关于 ceccomp asm 有趣的知识:任何在 _TEXT_ 文件中基本的 ANSI 颜色,例如 `\x1b[31m`,会在处理时被丢弃。 == ceccomp 示例 ifdef::backend-manpage[] 手册不能显示图片,因此如果想看示例请参阅html版本。 endif::[] ifndef::backend-manpage[] === asm 示例 image::asm.webp[] === disasm 示例 image::disasm.webp[] === emu 示例 image::emu.webp[] === trace 示例 运行程序: image::trace.webp[] 如果设置了 `-o FILE` : image::output_trick.webp[] PID 模式: image::trace_pid.webp[] 跟踪 PID 模式: image::trace_seize.webp[] zsh下PID模式可以使用补全: image::trace_completion.webp[] === probe 示例 image::probe.webp[] endif::[] == 仓库 在 https://github.com/dbgbgtf1/Ceccomp 可以找到源代码。 欢迎提交 Pull Requests 和 Issues ! Copyright (C) 2025-现在,基于 GPLv3 或更新版本分发。 ceccomp-4.0/docs/images/000077500000000000000000000000001514205130000151525ustar00rootroot00000000000000ceccomp-4.0/docs/images/asm.webp000066400000000000000000001657041514205130000166260ustar00rootroot00000000000000RIFFWEBPVP8 0*>m0F#"!+kȀ gn_S\8?_'+:ʧq~?گP~{B~{_`?z c#p?"߈~{kG_c}Gm 7?X~/O&_?mwۯ~co߷~A&e~K3{OO|/O{T?Os7f?`?O~iG?_??/???kS1gA$0/^iS.vRb':pA9{T\ d red)T}Ӣn,J^1пyX{lL-'inv26LNZI*"灚Q=leъDz(c ^D!cԦWmeYM#*|ĉMEzXw䕙GVcN&9_ُi+JpE = ܛfK41.:Sa*E4:5p5ӢaxdKo">өHT(qڝ3=Kd\{w^4K|B|+d?SjPh9㤔HkR Af5b;@c\N2WXKX,<뭙7K[<1ea| WĩQ=>y\'k{/b*WbJ6ǫg5n !@)\VV)H>+ &=k f{WNjI/A l^r7K{wRiuOlրu ?A&&:GWP?qyB\K.Eǔ!V"YӾ~ XL{aƉn[")zM6Z ,PEHHZ}$@O~kE4qٰ*k5B Ujh~+Ghγ:+f{z4ͣG}?-)dZt5\yR:69FSCAT8Y G[l^f޶HŽcQwhY>l.Yu=#p2&gdm 8ƻltݴ]F0H%8]Ł9%q@j/0r>>&8s5t8w0TտlÑ3qeoK&94 >J_IlBC^,Oj|2k\=RHّU%} `,%^? 3hQ>OO'hRYP"{#]1ߧ1 nZ ,Ñ3qX߼YFb'FUܳz 8a@1XQB;)>HYeĪ*;lgsaW:6nMj*1r |}iЃb3h #\dÑ3qe" (̒'zs# {g𧥠b&LU/Km?-%{2URz$v76 o0纐+RQvţg~QnIČVя 勍@ӻR.Yu=#qXf6Ё@>o#ϓjnOm꽧W^Knן,/ t˗a`Cd#S42a`Kfe)aת]ɼ9݇0RO8/3[ypC̺7M6Z ,Ñgz@>0MH#?VR\Vx( 嫤9GꗆI K E HO,~`OwY5*HAp™s'yJ`I/ql.Yu=#qXfφ%̻I{ƽ[v=ٝ7KtMswC*ao!ޚq^lOY,cI }yyqR ;L%ldMe\OKAvS JX$&i.Gn+ }4h! bﮖ39"?~(UEW#42#!K/rҝgMԦ08dMw?BS#6URw`)>+|57*+woՁViHeX?t1t 92aҧQr)gYB($γ$9DNR]*Ƽ'hvV ?DBKD¶g|R΂\lk PBb}e8%4f)on>l.Yu;fɔ4|qЦ3ETfv|_3鞧m^KC5 enS9YdYh oM`*hϮju%L,g-<"#hϝ#`{SnؼmL%W ;7fl.Yu=#qX> \WOYZhEa%`~LqXD;ߡ5vCcݮfzT"68*7tU@X(*!75aViHa|ëm UKr6;OzXOԫf8㛵}Lٶ=Rtr$S[SyE 9vz6m-Ӽ(.?r:}¼97M6Z ,syaV\Np۳h'P- 1~dp9JTuKgD#i2U %8&w{W@#FXtȝR-]~a E5N8/.;>%+>/ ' C'lc2YOjTo~%@P?7 DT[==nK'pjh3_%dS/M6Z [ƻ6v-&kEP/g[OP¤YXc v@&>U yQVs5ǭw]Pĥ1*`hZy;+aTa\|Z&ROTnH5!?r.XQ]6CŢ<OKX}\MɃq~&҈΅@{|sݳ#4N "Fs9jU-s`O,&O3"7_;~)ȵ BI )U{/C9=@ytG_3Q2Ͷra2VS9upt+TD@zvxr`2e#?5UeC(="LdForCOVGp@xp=<@"IŒon@A/dq7t)-h&&x! ,CmqTݭtFj+C "&@prq})&'(9;P!mz2c^0K;LD鍋 f /1FFyA,C" |^RCž铤E6" ݑe S/,Y:w׽5wXP@tda/+b1`7=(gjbC.+_'i|\P}ܢCZp:L$|GgY5Xpy{Vi7G$HuA!w%ӰS=8+e;9v0i<Ǫ; 0_T6I&?.d//.B [g(gݚNPM a|&Hg]8235029Z3EMHd6ui4LIe=l` Raap*y]J !{'ֺFپߠX;ӫb k$0/Z`0VqxtA2ayR']ha Mj*n„V8Be.VOl>G()VZ)LV}_V4ϰ.JT?UM!Fja`X%r*h4SUmtg[\W3 {( XҤͧ\\W%֥bFN3gl|2ڏj:eِ/s\~[ ޛ6k50"(TrE2J'%8^-ע"hm`2ߪ[PZ1,shW۞,d%2|@dDŽi,W#l ՕOCgUZA.- Byp,2 2V;^gf6_>Aocθ-NcdV˝b.W -taE+zsJŠbuvGL5lALor^UQvՎ[z10桙p?铞0W:n-W}$95gG#S& 寨K$ řh}0[-׼X2;A}'>)3Ev dH zo{HR+~bR3z!fϙRkeyًU+<(FLr~Xݏjb@1z] ;o&oDf3ʈ9X,܆c_dZ{a*T>ovzmk8yMX3͵Ym"vpYn%Q6LX?%cW:ߤ-MVE>v.UV-*u2tJ@h.\wٓ}jH+K-j_\|gVTDs uqI5/s|dX Ο\ 2%v  z :gW4]?-U䱧tM2,i*=cjm|:Cfh $#g/PbyO(ASIАc,J/ •l@?YClLüu9dbvWoxN"< Co0,8;F\ #@.=Yz]$O'S7$6y`8_f{w~QXirkV>zn:?h&ci2cv%7Y_ v?%HoJ_۴}aD !;cll"mxw*d/W%}òa{ Rg6Hњ>wIU1n[ƎLZt ->4 ZE*i<ĠT}r8;W[.U{k/Q@ݝy QUHRDr} j*}/(*8{̔mr!qVK$Lu]_}aCol bWa;.#8Q9]KH#[B\=ha@?%H@DpUb]u03D{W5w@@(#) "3%a3myׄ-m nav-Ydg*kHDP_,ox^g>HގO%^`% %;@j<$Q 'ޏ-4`M׹*4@ 6{+K=41vlOTy x̍ ɹG7ڭeJf0Jxď!F{ҺZbD/>WLmڅ0ҤPtz]ȣVsl#R=HI0IG+]K?e>Ü4BZC΀(`=x;+:2k-Z逺Fm7F 1zltmDn]_ai+*3{*DcOx ~ue!p{3@wehۅTlXK,Vi ߯.G5ֵ ޔ6B~h5²c*5]-5IJ+ǚ1tWY_M8ص?uid_ .o/A.w( Ҟ+L۷e}My?qJi5VAEx_V/ydYE[}⇒U|@_2' $&UǪ/nʆ"QYS)lfsqi^.6O"3mLP<@LBm[D%KUm QЌ$h&vMdkfחq%OIJr˹G?.U6rYpsƌKW?dRD |CGk n#"J MltKۏ 5AZ[/yQ =l=&1[Qb&16ǀuM)7Yִ9/cD!wH8_%.Bb tF_̏; z6f?W?PvK Ze6^{hŪǡBMvx  zNL;>~(H;ɆŭSv}3 ߀ w6 x+`Aw䣉/Wesf}c%pc1?-1[o`:1f?E镐sxAp1p߯]lc5PǾ%=bT~肃7zSfIU.`6ݯM "pC~b[TܖlFzvHT-ͥ-z<1=dG˕*nInLoSW+. -kg\l*=%H9YZyGfǯg  RvpAȅ=UPT4FReSˈ7~ˆD8{=#}8}BkG"8 z9DϷ^'l(L\B,{Xt1)3ߜ=}|x,Q-s^L;Wܱ|X2ԔȀ; TԐK,ﶅ3P3Cf?PoP3,g0Gb3#,zqJ ~|Fd4bhOCt]{ySאAaza׊G CIL/Âa>ryMz6DN |94~؆39I@ 5lKNcgu^P.e_K6!jF1"nx:!W",WT\BpͦNlw3jSs@). 09lڒnGYA.o=cX 5r!a@IZ+<GAIFߟEvpGwpʧ$e|o5mC&[C\U6{ϭ "d5ךGK^a咓Ws^Go_L|"Rĕaq9w :?a4)s65Z@Icv0jEr)A$ vɹ|{&Eې Uӂz'΁n@# % 芨njz! 6߈"^ʃ-Q!桄Rph%d =t#H3#+*$\횻wC"aHQoA0g$ *rW+;IS vIbZ41="IU v1 zF"`V'l) r˻3  +k\զߕ{;PlDv͏k )(~Nm]*PtjbQ0۬!~ie(h~.K]pmCPˢoZWQ8b77;kWKILHS}(.#xv<:w4-/h_w=V w OdƔ`S4035KqEfVq9ADHVg!w]YE// GȿV=c$ ZQS1QM:tk?*Mx Q8mjUp".#eFsԌ.WmQ ':$'rsg?Cn+JQ5nhbU"em]a}[R(Tnٖ߷{R{9c}`uj`s#ƅ݁::_xڡx3M]uecr=d,'3]Vb!d!(`яXx/\!a1RvONz~2[*U:ǴnmMu HMҲ8Hd& ſfrX)B4m@;Sn+wXSߦbkrt| JJatU0ZrEbؐPロSx߿qcDw4RJ\1.!\B!zy~@;bTw:fVR}J<D2]]~]U ʹ2qsz[wjz!IK>k۴68*ϸ<߫ȻRxvlyPb| xwт֞ ~pEH+@W>xt7y|q7_7 $W>?< Ԝ5@l D8|o*L VDSn:2}5ðޗ^oW/ G{s:y!܆ֈKPĀ_r9>e,higӧxwj Ng%0\b!3eME)m UۃQۣDѷaX3@K/KCkzN-}<Gw.-zF xR;eG2:b60B[xEVs?F\(^ .ʫs_2eȓ(ceS=?D |m,Xico!ّ-iP]_VMGRF$-=rԯ6g$ΥRͥQhLprIsd*6K^XFTő4cF"/a ZT21ҎyDи=gIk)ރxݼ"ھ'DKrH[AvR-D Nrx= qk#4dzBtE>ecC8ZxHRkwV=j7S.~Sع9Rh#nkCJD1wE/ [A`lxf!^=;lm:W\8tGW(#a 3pY*,Rn~LwRU t|p>2yg/|D8lC4H6=_&L.G56&˅ߎo /׹Ot80$8H:W i!ޓq}RxhO9 )<#/]q>kd FhJKq[+8r8rq7.5tTpAg, 6i3~yJ5-%|dqPRL"6:ȳZ)Z$9{%UV?M</JmU"08w4R+ 1`/Y Bj2)ibj5w6<چBT{cA/fyIfUSs㋖KmKxP2 %.ބԘxv:p=;^2ÿ xT QOB d1յ2ŵ8OJ 'Vt!B ͍ޒr*mʛɭ^]K5 R߬ÒeWqNrOvk٧-gm*٠ϢVjioĝm߰/Ji9" ,Z,~a,@4X_͆P6fvџLI#o g:Ha9!(.5//RW"Sۤ:P=7n@9[ }GЁW2#99a[@ EzU$M;C\P. tWL:ϻ)o$иf~Z_`<ѩ(NbtAz=8fȀ 5u"1 0HRe:ʂ睔E,9jVH^yHaVY2S ˼X=E=f4)ɒKlݵ2 (^Xfw~tʨA=Omf2Bv1%@g]TK19|KBh\%QQڢ-̆UdAܻElgK'!%'s홗} &-.{Ǝ*,lS/ k ̇G,q'ˬkQr;jQFW%HT9 Wa ݎV4mL@scLodIiʀ\I؀P)1i+ o.P&3ZeK0\7fhI;Ơc[v+ObbW\Ȁ{:7t+AiF=_ Dĵ@9QO Ay /-xIKL7]qG#vX2K8]'# <AϪ$;yD0r%)1 &WU'U9yes偐18b:3FR4:ԫQN{ N1Ys2}lï*Q_]X( yX9ByVRؕb̙K  ĐK[Y.Fvzݨ"0=IJn2;k)xs# 2l 5wT嬟:F]:n1 +xYqW$DLQ%WA\N ^- 5nPuh;XN I Kôr AT5̂٫uXUVv\Ț0!L)A|d w2a"7Hoq3x { =|jΡdΗ= ~%!)[='w4T4'uY1?iVas>A3V?ed/ "Q+@/ގ.䏼hNLTJɓg9ϠӉ5;}xe؀J]EU`v:y0޿IkymX6D#5OdZd%U>*A >w^pA}XStH>NzyI55jӔ[::2$3 i;}sO^LyE~/z-0m!=}n݉@Qrćf3<$&oC0ed4N%EjA96i ҋud쵍(jZ)~qw$}aQ:\śTծ&MrT͟׸diN"z)-?P12wvk2XB4Jus&mJ-Wc,s0k 54<, 'f?uӽL8Y{s*pl.AV,TcY:|y`qprs(rA\BtM3ͩ+qWiP}^bǤ9^M8qk!%*](T94mf',8zc|*/&DS&٩Gܷ0tbB˚ L@]`KV) KK z2Lfطـby8Yqx/Jǟ淶}5C݊נ㢩JG^qTv`I=RsC.v$ 0Vt!M1y#)~OF1A߂ 0 UqJU@nOV#a.rEƥqگ~[YZ7sި@Txl1_ *GmNYaGͭ'=TL'ڀGW`8$V{_ nl8<ť,tG1|> C8^L?:'WۃQ0"J ,6Dq M}ǹ);_ϑZ~VJzUZj(&:táAY5)K0Yó{w}q OGĭ(Cbߋ- vnkBY%$B(ȇ#Ҹ]nI9v)-p wp@x"Hct, "* b_3dGKr* ыrR/m} 8¼j&R{a)2 0Ei@"&eT }[*¥z;$ms=: UW?` V,EpЦٟSE2϶çIQ%&aM`m=^]K +5n8'zF!ex^L>7%p' c?'~:ΧI@ o5E3^Nѯq3>wD$L "+(a\w&B?3m2Qsؔܜ>+`qM#8<h5iieJs.bL*3Hz'7M& C E>JμGr0[K1H#nѩ_7I_#'> nxnds~Z'C|0Ab^D8XXLnahG܇SB1/nsN(?2֧+@HGȽ;u m&ع߲a8ϝߕ(˃tRE144n`ԌzPAr{woR:^Y',mPJNlV -Ixn6^̮[&dׇ XȋﴱA||m0tN5!C~.[kqCx N=+^4`xf{dskjßPSjaLg\Zxpaѷ =m2TL!KϊOR {^YȰYh0ur-a5صnWtD-`Pl.wUNt!WxD-z2 x$4՝;3rj84QV-Yn\|%piib1E,Q逻SV*)1 ) VUWZ ;۠o~?F 춬$2 7Uy}e7$d/`gAB]ixSܫ婻XtZ6;BL{Kc{E'uurl+*-FK+MeIſ# 7k7Y늸'q8'u﷙yI^ :A0̠B1v4kݚw09v4?쪚-~19i \HYA2t̡Xt/:ܙE8`"BL6KΤ oI-Wr-Nv7l "79tV %@P 7"nȾ -ylϰU%T *I&8boc"?}+RYp#} PLYğFaTaC ^O{8|.<ZY;>hn ["Gx3 gqW9ȴ6}״qBxO@|Ѥq@C-jɃ_?]e5+lܖ9]TKWKzW*jsN!o۪yg .C_d%x3q9T35  2;>1_;b ~ܩG2z~0DaK-ۙ*/5CeHbooTO4w[7mwd '2=8'}0C 47}%Bgf0g؜?#erFlxbQ@!M[  Rd`yg}K^9,>*ϓ{ Ff|Kq$1'4a)ໍ .>F wDi3R(Xip7LU7gbΙ/Г1K?0Ex6|XFI̸1aю|c0_EDEȤÏ8LL'6e0׳g (0|yĩ5! +.I\'VӎU[/Kv' |ýR !ȍ]pj| H[2ovE\U\ʜS#&^T=lf,'HsW-d٩ s՘lcݼ2hϥ Öز~E'˜|i1"'M&䃜 @v ! ሇAy\/FxR;A 4O U#Jʘ3\`*pB6򮇒8> ݵ a܀F$c.&.m@..wѵypN2ok_!<_ IQjnrQoCt@yycz +49õ\G;)p'HnH~BqP\ `D̯l<]*uc% V˸h _uY aar)!U"HfF(`jAPy'c_4o?{[ݝcgnz>&b\fU[)/]:f|zN(]R?TNoqo1Wh zy{2pHX/h"]WR S< 1Nt`co1~~ rӠPƲ m7k)g6{7Õr.ϥ-D\C9VAȫo-i!S2^\ +reA939;c/wgW~%M6n,#XyN5O|pCf$t%vK)T3ŻRcYa$d(?'k'zd[/f*ڝGQF'o9Jω% LϥwT!ɍ', $\ m>I4b>,'1KpS䚄E^t@\ܺkytm b ">7S_$Gh|:Q2T颶:PuzT/,C*0·i1`)N7gg6UNdgqrD;]: ~0l.a]mZ 0ClQX W}l.ɋz``<\nŁit||q% .([=04jlí9^MO\ȇªTvMrFxΤiD+#g(\D,헶9}#`45 >ohrU(؇'V7p!5tmV`S&F vV%<1Ҕ|.xZѣUq7Izhw(sPÕ,&-EƫX\b&3CEm"~@Yc=#S,LZd/U/ъf%ÃF\,i  d0-*qvM7%rf X+{*r+# 8TO) tD8YF.gKQpijN3L ʲ͏h3h1`wJvL(gW-)k hSw" /t7r(C`L~8)ʝA6ѥW,.Ter.R@wN4T5&Ԝ_'G Cy~wokLG͑]N丢h{S. LJ,X2Pܸ+Dfwf-DԵXckj? }y? [mޔŶ?&V͔&Ϲ5z)@C  SːkI ̼מʰI+{VQqT=[RՏ6ӠEtȘˣ@nw{AUlm m[$Swڹ5jv6oϹU6^%.\ z\6 Aۉ3ѫÌC\*G!6hteh'ޑ 30%KBh\pXr> Uڔ,%Dtyߴ$i ju2@jaꉒF--Ӕ4D/SF [X#N /?,-=07 ]JѠxPqtXE)PPVǍJoH[wI v*8^>0lb!mS~fAӁ(`ul̟EΨ4mEdف'ZL`y 5C6 ?P,r"a[#eEV_I|BE?FZ)q{{D=xl=$! !!'$zWHyT 5#>L"`ă!t-iP0J$.AB]ü{E1 qۃw۷0uyT?5eInp`욕 ;gmcGR% ة(ʰ לfL w|@,/;" 5'56@8Jdfo}p_1^R2Ud8BD ,O2:%"&[Ψ )H>z+Z[BAEk&y"3f 0Kr6 uØnd%wEK'|;pà `԰J]n\-P v+1/d.]5M,L 'YkO(Ҭ`.݆^||05RT ͵'RG7e =+FAG)},ƛ40*t vtfmqD͌y$:\wbPY`z6$ޝ;+Y!; wس)qPLd5] f dk! W<' T?A1Jg8hn0*hY+ezh:ӅՁA6S&/LF>Mûb0>L1<_*sO(cA.k8vX %>Ig*1pa>Ȣ01vyÑ_|Aܱu6y&!K7o#4qgNULO5μtj<:5ѰEwVd2'2j AQlL%yuh}G\s@- FfIc;3zgҴIp[KL1>b,n31*xM* sURʟ?Wki`crlg¸'>op覛c=N1d1 <*iޝahe+-(%{esv*#.b\G%I=7y/P'_X1=sɲ h;FQ٩|LwS%Bgǵ5GXVyz 懑"}{=gwy_8zԾٱ9IPWǦ)\\rc8%p|PMO -?`p \cc4P꣊6t\E7 Cpb}}J2[I;Q@u\w'N3  r{mwcd[/* eܜ$moƒ)Gc.s4"֚,o{R.H/׫fМ )g\7/J >J 8 -4 ,`Zw`~Jnˁɾ9^Q r: [k 4 9'CWՑ(7h~!Н}ι2/_SxaDOo?ZPN&凅ό?j@8rSiELM~EPv>-nSMueO f]5]ӧH:XnI\@6^Y~.p+fv|wnJtZ1v:hE1c0g3"=Y"DiXj$>4 RQܟ~rOH!##SfoQ=uĨ ^>uJM+Hj2#@lO|J L+&iMd7Rp> ^5'34h#wfw[p ?ɥFl0ʗUԑ"TڌށQY3ez+[VAUpc&+[0,#Dy^SYw/{%N‡KPQfBU2g_|k飃XilB&ǖ4b<@ D~*[KRŒdx盩-\ g9U6fQRّ0.*7CB'|,YEw~SG$RQѨ2=PpNo5 p*_6WGw#*#ZoNШ޳h$\+::d04jQc#$x ~߇.jц XknLӔOCL২ځaCSZXn? q}9ѧA'`y|QpXwM7|?_mZ宐uv!C)2D$ c%-zAB%L-`ٸZnT:ӃwsV9zbatg)/k! {CM8 2e[q lɰ IGr6(l|K Jd|W؛=u KbÉP6i68FJU´qWRT䪕W<`m'G%CGAݎ3 1*x?JkK;ϻ4K{Y tZbH|XA:%1X[o=v*ɹCF{pT`3(H$]*3_NVrZ { f7")*]L}C9}ߨLh+is_(|=pxn .au|nP=(LL>,{ Y(fįׇ!@u q=W {Ut; Nt%q4x{/k}%/cKM Q xgNR{3zUx+x*9f@X9\ ux쾧/#HO" cdkDŽljyi.;Iʓ=N@=*hBF۲/`x4G$]|EqlT"ћ 6>RYӼɔ3 I q.Uo@ ^-%c)#暕qV~Fzgk]?\|g?_c|t~4.73 m) >]T;50@\}bjZ;@"o&XTTbeWw/Co>lHu%5e@Dij `%߉Er!ƬwIR)F_lPFѰ9Kj3j b>]rm[CE<3KN}uI0qG B J 'XfW{ځ Q0X Y*#ZDńtw^7V~=KE"V,dO',($+f%"Y9:Iȼ @ ߉Sh%u"\=`I]IRЯ ^+ل;fzSG3VN]FjOta?cp^Bn+ނq7X ZƌA B=v6 Z6RnmVmp߲#Dhz2aǦt]C]`H(01? r7A]}J~_XbT_e5⥹)5, _&@iE1x!Yq'2KSbs jIlj95өHx10# j}>Bʐ6fvp*ƅB"$;ځ9t+b-C{Jr;YdU1Wցe1_7Hxۜ@&̠晘˴&R$ڼ팻s0%|"7f0EF$=Ic]%}uE1@27LAVTRa>5t[&S( %;)Ҡ#nv7 o;nsU<2]oNdC$WZ@nSy[mg.2%;;&aûVL-ei2h|&Ia~h|wdr_TQL\I]r%{B1qVl"rތv*B^ݥ=8_lDxdS8^-=:>f`蒷G*7Z\|OZaoׁg{P5tNXB.O=;wsc/?y苼,ȷh!"Ïu+ga`: 3"[>KbynCqS<)lo~̗a,h[ck8 bø+pviij8>n.v&htV^- - pLGjm97C3*N9(iAc+?ˍq|SYz ΑJ|:ja9;+cE]PA C_jz54ʁL<XrUJ)]3[_r ;*]'c s>Uxq;U,zEWQ-9rYHu1?;2}?R ޠ^O Q ߦ8<>2 Cӗ>Uxq;U-JoDs1cgb˅} %F E}xѾƯbBߵc#%Iv3$߽}FvUZxmA+1,ɔ -NQov@. a~q{`v`VJ r&VEd6ZyN'C^r9% ֹa vnl_20U75Guf|\RE!4M ,Bß'5Y}mUdND{i޻{s./Ǜ%cBw,PSR_{H+u*ӽ@$_ #L+{Ga}u=|#@6MD]` HR $ P`PNa7*U&W頥" Z9+%~L^ifX/^~?M`xS0* ?J 7a;?T3lb4 κ4xka(Vo ⋼mxTe}IW7.k5\Ә,}& roy~H2%Z7mSNܡj kKJV4 `&V, /`{\HEkwJ`}Xz& 3c|Q3u\tWt8Qqia- Dv=cuA x|x\ƙB,Y{KuFY^S'a&әPM5#ۺ#;v߾r{ma $j:{ہ4'^R\rt/\Tf%gy|1 '2`X}zʰgM GKH~V }DYF\DAT:lOd'&*4'}LmjxdSlJ(D9FA 7i6*eS4?&l7 \˔AcT1WߪC_j7pڧLv 1yѪgXUPE8Kkox. *ԘY';'~ʽ_Dy?yr-r Ʌ oݕaE:5s1q͉~iuݛ7$ fgD<n x<__LДfZdq{NNDvDTj@?LVKۓCJgE Ŵ@C; ӈekҍ% I)=fel#vԛ!$lѹiqMxUo}J>J(ef #AH/?',N.&*6 zbgL:o~ɣR]Kq^曅y薶 qu$E0"폢Kّx(Z fxr'7QT;A>loJǞsr׍Һ'Vbpl7/S<y{G ?s7:d9iZ4@`C4|J D=O1Iiij†ND0ŭe Fab4|';V*{Qԣ9DeC9Y㮬ʅro,&Q('ϑ]7ŕWK8g-dum9y_z?b51͛R ,atM5ލQK.@!ғ&ƀcӭI֌]zrÉqNYt=\f 7[鳹$c$*jyT_0H@X83=썥QV'2䰻t5_ZWپoWi^h|욹]汐QEN'fcAOZBŬ}5Cӫd 7n-RmfRl d2N}$,cpX#c' '?o2ٽ&F]4u|6FvPU:IQB\q#`%A:+>ڹVI[~Мo krbY(^g'Y>rxΗ#ãWiOhK|ۉ))ŬmwCND;3ֆ=l,,]`, 6[˲^r}tᤣO#g0#'`GFbR4' `9|;rօ8QKc =E#~fyۃ[+>YYh<#GǸW|q"mԾעcNV܏(l 2^|قǜΉG|K*x[eQ7oFik5QE\ۄu!l4O6,،998~胘/JsgxI2 P?'jĠVPl!B8#H="Z4_Ɵn@o|^DTJTF+J^O-i\`F#ƾXQCRJ^d p{?^j!C@ў@cDV*FwlʉSKxfo k&_È*iN3M>uMZu~"I?5bR` "$e k.j7:1"/ĺA"JSGgkp m[Jx"/VyÄ|v P+sF NSsԈ7G2I_(∢+ׅܵMd 8z`_kB͍T Z.C ze-}F} b6Ro6|0OYp`v^@edO T&jIW`E3l/SJx@~0 s81AԀ s$d½0Ff~B!UG56 rDBW?P4FS{|ORٝqџ̓/a^L\j"*iƗPy.<gFqܳ,r-m5~m w H;_X6Gwq;"7S[܈)}/Me'Nš5mcm:ݾ"q'+`/7M'骠ӟSw>rnd3# 0=C"߃ M߄QkmouKŹ޻yz;a% MCZ8aQ`MA~LHPb\+>i3Jb3@R6tVE3oLfAQ~va#GWaV#!`F 9ڽ+6Ahh ek| ?ɔNT_(",m!A%Ngխ-TN/ 9No!: 1I#^QO] ly8IA _8x wK+ǣdThgV(L4vZLFc?>So/$N/)`ve`y (@U-]M^LcY GjO8 GuoWչ4)u͸#,oIFRAkU1]s /auCfN](aP6ږ9ɨVX6c([\?9zflM1uc#5![߭W%0(FMhZ̅:˼|e ):54!NWՐn+JMKW¬Wxg310%4)N 4R|D;^`)Qt>+g⢳XEa5߫,l#gYY:; ԞpV n[[';߫:~-贔B֞.%,/[uL8Pg U4fs8-=/RqZ=ۻQGeI1BԽ)5|b!M 5nD ۄ$= њ=My;Zg)S`07}HmؔZlN~Ҫm٨#[} F >@n1UD'@dHHfYK?IHL}&_>#XW`<_kn3ZԈ2xZ`I“)|㥢"i4*N#1WiKF Gǖ,pFykv`F>ԊB#s=wdʻ34i.nLGCHTfU49z5yK='pcbZe(* knIXxxI Dm+a:hMPK/<­x)p҃zG1Oi+:\;8-w+^ʠ[7fJBF'oe}*Vd0q W6[Z,8qHJܒIf" kAQ}3B?v_7 74:áOSdN|*?T{|E2 X\4?2%"ޞW|Nd*Q7!6&=u:h>*fB0 \;| Y}|.2]гu(SћxV,kg$ F$6ԗ|b<})k;[޸3ˤfA) }J%: P$n/_;c#N&oA(T+AZS/ěʰFʸAv2lheD{((Q#?_,~@ (%SiON&#5 pNOg:frG<^zSBݠ42ִ]{E"]뎡e*PK%@&/ ӿ @V &se#@F ;t&)CqɊ]vcD1nmauKږ@ GPTRy1@mWf'YZ =G1V?G04;u>zENf#~5.r7ssWIͤ'6~^ 4gYa5Dzn 3^į|"D#USu2lϞt@4EʟJ> Eh2U̶2l|ecDT2G9E| v8"MR#^иIGW4 _ơ[Wn^X1SOC6VB]7CO2c:1#oyG^<߉:xT͘8Yl^/.JOzȑyKl _gK@gvv`U蠊F<)֛q|u 5>7l[8] *٧T\KظVkk>SMBTf(YOmP l$ףKG'p @6tTf?0!}0$ٖ5yK,&X D9oQƕ(OM>q4,"zv³^,>>>=>vHly5YAP`IΦ*]?{ ʸ\7I9_DNfU%r+Riò2˹:87 An԰gOTAS6j#J*U0iDbhH@"rN4Z<ᷨ\5w7)\AgC^F(4 J[Lj&珝eUX2TU$wn1O d]>786cmiÂuIP;VA8ٱz1G,ZiE 8"ݺ6:kcu§EvS˹"{(ԓiΙݕ|j[b. ˥YZ\}#k$\7TW2G7X'Ptʀ$h)/-f˜epDESŦ*)Kie zFA}#6!^k$GU(%`cʷ=N^D3Jh./avw+'<ɸ"uh!J2KIH n9G>{s *݅n:EGsenJSi`O}lc*='B;Hwu)dTrv iK (B ACL.?my>zFA+E NlɊ,Bl\SkIV04o}bYf֦\irp:.|yJh?qpœiV4l%u4\׾ugaZI<^giÇlQ1'-##q !4:ʼn*.!j`2 XA4=M9Oq-K[(|oFVAgf*̴ؘyElT^ާ}Q 9h /O\>$hN|†OT2@-.=N>ShrxeEk)0NGh`(tfrej\4Y *e0%2Y^9 eg4֋KC|qaUi" BTȶ\6z9Ӌ8ie!y%@[&HݼYZa-&C(8Ut]1Um׈I.GVm c6XvX 0I6eSfqy[]ޭd'\ygՋ4_kN V0[gv! E@ 5o§> Eϡۇs9t>0QS!^'y^@Z0R|hdV4wg=ވJ.P,|P 6pZ)7b;ݐ^fgIP=Ծd#Pkj"!禔6Yr62d]Ff x'wD:#?q@)H>f%[EI ` Ę,o?xr l{0{?&Y_?K"[#pZ^Ie+;wx0%z c-ves`+7n䡁"5 kH)l^O^R/ o\\mSkx%Yfp3H~)6qD)@]3-נ\ͯn͚tw+kN~(7 5u-=[k`<`vߺVs!v0i¦F~S99*`\|g 7[2(cjqPq/C;334;AKC߱ZPO[M^k<[) ۟fTO`}55!imYm*߭ອoiA ֯ .C^\EuH8 L[_ca@M[ס63Ew3tHmqxVN sw|99PKA4iI&$V|wBH˲gcMzDi5C(V]giu}g{K_<e\‚|At*&ԇā:5,<*fͿ#7,/ϳHGdglۑ6שId2Y%=e?@7 3n"34 Ruw&ԿWQ貘RplwU~ζr! 4@:YYstZKq kn)$mk=QT4lc>y|B.zBnT>*IN|\a >н#t+{o#} Iӻ+2q`8`r%!^G(6AAph jdI7}.~|$*#Ev#xS*[%q|hnF10J!d&,Z?(_X?NJW: n_jT~S{!Ў%?P.5}u; a(6 T>*No]z BGBtd\ ]$%ɂ# ;fN60nQ~@1T\4Y5p=%b<*M.þSO\ZGQ)c{ODa ė+3}~Ũ-=)dgہW JG5t\"@&Sm'GBJ0?Ah@߸c{)*.|[w|~mB-o0ƶ֐%}ŻGվ};iT?H;$ݖF :N/Y ;hJݎXX (t@>^`2\,X>?S+ql6c-yO3S&_*YrJ A6g]}c~y#x9^aiA<>ԑ-;>0؍b|BoаnxQ~GJ,y1_ Px5 (!5$~# I4B_'_x?XS"4ɽ;!n5s>ׅ@gR ;~n1qk#T Kȏ7:_x݈azYlN =X^sA0{ lt^ZBvNbXCusj1Qn/=s}T*x,%܉}-OWSdc]C{#&m$b&mLA 2v!%өі]D//IOOYJ|%gCe|+i8,W5&ih8>#ƴ/Hx<@ŧ_,j$?ߑ1oPbR\M}yB#7ҌDPH=;@mxe!G4F3Sa2CzǒBqYXR[W-jxa>+[t=eM[^IÉ=H3;@G)sVtn!yc۶ZQysáE^u>ALO%CUֽ)cb(R \$ҜF#WXjp YN%`a.x)fP)=cϊRUgǬ}rx2,t}4VukF] ?y{D@fj#.֝Zí ^=U|Vd)DЕcrRLuuCuq!냌vm<Иe.!hNnW.ub,mr&h,sb[_SwC~7S6?,!.8LoapCvK AXeJsn% &me#s x4H{F/:]ruaP~lPS| XtnX@ьJ-e 9^:c9mfPgC`PG,Y0Zd̫QĈX8˪Xِb( okQTG"zR@"N =+:MhʾjL*|lkLK!ȭΦзÎF{R(H¡ ,uÒVJzraVir8IhyN ;N5gr,zR+E w Idc6p،:]w}cgh1[ZJ#&õ?.uPS{;~.2^p\:ZɞϚLQ?Pr ~A5(ӇPE?NЙPLOUob*ț-^`U!lp?/E֜% ~K \gV)_4Y7CN<,0 h;]g欉Vy"Ljv|kLvW{G$k$5SPp1?UV*0F;׺3uNE_!Ը[P1UbȎ\GP)PS+f1vn5{cD)wgj[gNvs;zךocmyxcQf ܲ>:7w`gK"dڨR(cŹ QI $ghjk=(Cij_ CYvC,c$W (RRmZ"fiiCXRվo(`dfY۩>@̂ 6y<߆eC|(p}/<#v &zk&"ݘ|w R ̓CnKAO3o"/Lnf;t eP(#xQBHg9*lYjExsU[LuɩGķ+'s;+oE(gaܧ"4 ({ } k|IL1]4ý%֘7ws]Gv2I64<P`u()H x_[*m+[Sк]"t@7y3=vO t2U0'JIƌ*0 @$ y]s?H]^S' G=Ӎ]lՀhٱT1Jz^_j^FyW(slABYL5*/Ooq bdЪw+>FpNa.Q:qJ֑@E:xFnRٞZ$aDo_0C::. qv+N-w~e,ze [AyZ.'2걛U]E0νrf9C֟K UH< I:/>Gc~K濛.C,R[G+S3e?)fb?鮚Kh&;:vDR˽y)o 'OCu"Z]իqcvgpgx*xivpgς}kֺ wl~yO܎~e;5B#G4:\EZ:0XҰ2`G;lƨ~P{PVܾ#1GHB%¶/a\>=p[sgS MNtTW&1aN$(r/Q0QrAsrf bPÜs)Z @S-3 5ѤCg X ' HϱDI,,Wx Lt}S r&@J/.yuRd#NdYMWe!@?(UxEt~9[`ReE|V_dtUXoҥ$+H $w_s,Jm! mTA{hӴr:rT3PnEu'O)S]]_w0eEU\JY;Pbqp?K?$I?A6hT,tݖ ] {6ɿdÅF݌Lq%g$ZL֝^i0l0SY<\Vw9gJ6U Len?yZtŽsEIsmEU}&t:#Nac<8ZVt"'M^lm;@3: A`dxe[4b喔6Qx-j\@f-zY JmY:|T&a;vuѺZp:Kb/UrVK4>]3Dhh`b coG96& cA"oD睗e?SicȐ,U3Ɏ45O\C^kiqq{ mnN!=\ͭkDWKC;Cÿlu?{ |p4Fr @rfma`b:FUueV±sF0@y@U.kP_El{O!m?"5d'РQ㋺w.GX#}Ih0bZ u!.7)(U;Vx!) t["gD^;j^sT"nW{Cu/%Tkq;n=1TX_I\VwWQrRpC rH'Ȕ`AÏN5–$z8&+E !=v;dt 0Z+ĥuG#Y&{JU!TF<~5uKZDFeE5[3Y|Cg1pG6؆\JUI0Qv%zۥVsP!(FKe.wM#@ۨԎ?ASĺf?nu7FpVu?^EҋΚ#DA2;_֊LBs T-! [ˊǛl!rYr`Z-M]6,PÕ,w{VЌB%Tlj:AȮ~E:MTО9nJJg9㬛{bikj.b0ZL*5%) ^Q-Su1 dhP?p}# Wq`Ʈ(veoLhA@%=he$ лnN x&msa:C f2Lh&]_g5@(Eq| P日6н}6|)dnXc#ڸn+Y.59}g޳<җE Rf)MÙLqɣ۞Jm]ɟ۱e^{PjJvJo0SxgsTcT;Bqs KLʸG+#YbW&KO3ZV{!oqiTij.1KynߏRjBIgu .(ǿ͛V@B8zAOiN溬3/4eh{ڠ:[Oq6qcq43w+sJ #=Lɞ,ZbNߜ .u?E)@xr_ehLr2eelV.@sb' ^*;41F^-Oj\= .LД=5붡A/'"{ꧮnKS!_=|s ^RD\CcfAo Kq<wk Q<xg8yCPUJO37J ԭ[T\G)KjdJ&~f `-h!8Zܺ@r l{[}Sh)jDVWj^6&S* #Q#6z"_a^qnz+M'kW~8]9+_wlXUIGl2U;y-|~WIQE[ M]_0f3[ O"gu5[`^nTv'u wmPk;Uh"bhe ÒHG$`pw>C)'ZUN޽$޾ > ګQCt,usPc"%Dt3W(wgQMd]5>y%Bm-r)́fې߿b ^Y8>xidGN4^ہ72]2qLE]'=Ev3`x5lѽcWǰpZ3f.y77e}6 +֧f8/Y)LsX_{#ivT*WS~A}H % Ï>9IRCMjٝ$%K +}>uИdQCb26Scȿk?hZykjd5NhZKՋ0;;n(ch=0fyqzH7 g#CҾʲaspIq!DZHk?adD./Q玡}t VDr*R3hp40;KT鵱e͡l<!+ tM:sZ}em;D ]Si5" F{84=dB M? dR$ܪ,7 ֍̖a6*oDJ4XyP ӆ[M%$mEm$ /ޯ $YsbV}㊱1q 2ru扟8TKV_ +?_ό-@dP]Wm%8)PGW|(\㊺猤+i wxN'Oe*&>m[sCD}+IRT)nϯ\a4Z!TH*dyꟁCbOxq-x!RM\mp>%GGviBUa fGv=;_(AQ]s ͸&nZR$@& &µz^>e7Rv6T[y'tWrC)ǭ5aڎ Tj-Ze9"dUpœ]Y5jfsTԽGGݴlq|zٲ?ԉ7̃0>U.6m=%oʂ0ݸ'o&x\1){;J1N]1_B+}dx@=|f^G&M}(C Gn. -FÚƃE)VFf rZ,^ fr2] pvww0]&ywn4$̏4 6-aWzʇTZfF_kiɳ{٩UGM[~D3rDjC4y̴QC 6*WeI%z\O*4-R"޻K (4؆z~N#IS99~{;U̯Dl5O)oRe5D[e_o[;߃ߖ=$ Ѱ;$;fm* 4!L]vVlpE ei+JHELρ GZ_r!ţjkb 58ha;Z2G7T9 j%UAY8CF **cQqB,8<TSSs+4{tM"l;3[iQH;xE$EC^gpq Br( MezdRKP*sx[ef%O9D͊Rhd7W=Y|LHO^2̈,XfIRh.CBf&t}ӪvFZ:ozWv.ߏ^Wūko>/@n NP5A}xܾG+! c2'al޲nI\C)+>^cMppF=biIKB]9G-U(…hHWIz"_p }@?uzP5iVGJpvH}[M(vyba"tV(5,\&q.xh0F&wKU,ǘܖ/VmbjF R 0?GO Rљ 75Zo77rz'lż *(_@V8Rǐ.*DVNE:\(UE_h,pZm&Pc<|6i44,ՊҰՎf*\{{:]ꑛ'jY(/3;|+ bXx/b6|u|$m:+0{+AmqeC$>[pOIcU~Bc5)d@mohËvIF1ҋx%*-Lѽ-tx#/Ca.r@hQgw~cU:b1:6soS.%gs<qڽ0O)V|(X tV$Y:˜oVj(_9qq"$\ړHjP!>z=;®6-| |`*ɣ#wCG HPjeg}NT/ſTN: CJ#"=+pV]'~xdj&\x_xnӮ ItךO< ߸.q3{+] rʈ9'M;#, OPdS8 .׷4q݇[G)!@G爢U/3\Ko@ZU&țB Bt[_1-cn2*Xi$f>_, tTR6ެSVϫK+Y\j'0k=B >̱r5$ `RǛ/ o"#4!awvgnTyKuN+6Փ| ~:=v7k5?N.|!D34)둍3ueٝ61t-PZt!9reBx~)Xj\g:-v(9k0( uYj1D qU}n=M,X 6kQXR;݋5c ߘ81HlFRr7;i)X[5&uşm@;6$ZgV{{^]ZTz|8n;#5Cc޷}}PiXY9k(xށ`φ8[(i$+~vFeUwx >7S7_ޚ +H|ؤeh7VBp% qsNJP7GS؟H)|9?\OܷAyokfxFhÝMNd.A!" H}lA? pM?ļ(m>yV 3.yׇIY|h3 ݇űZح`"2K43oeH!K&Np*A99fMZAc-T= 7^Jȥ2ݰq!b9QE\g3g 7x3JZ7T7m̂e23 z$iwBQOS0DYZDOp:@Z =;iS2wq!DkY#j $-LxϾoE2Ϥ3c,3a7:iGW=W=|h'&m?]x嵤z#dHP>[{XgCHtݟ; Aϝr%x\mk.iZ sx̪zo#0k61:+<4DůW"M秌W\.nHj\~)c2flZ8v|a\ *b`5.NndNe2;K:abVM2!“L1 EfOFRR&̃&/K*1l\T@Jɘ ?u7CDr&D|GΆڸ{Oc60r)v]Gds. w[KC Ά M[H$֐} pcKZy{z$,_YXe.DYdn)HDR8\9! N(̋gX;QI| <ݦkEo])Hr@<68 o7X 2v76ťnjkgco+6C0inBi}fpcJȎ lv:'gA SMI9=f& /xRÚ ƀ ,TE@#9HjAa,aʸ&^9ϑKH|~*&Gn|Y{TH+/*B >"׎1%Eky ~(mb][C}4 HCh~o7Ry*^[3+.G&Q1G`jXTRm>{IŇHcaѥ,-Cv2ųVq[SG&`NY׺?;ѯuԣl \d( eU?*3_6sC \:ޜр謎j ldnW#?U)H4Ąq_Y^cS c:>^DYvuSp3&2F#/j= NLȃP0[Z@n$k|]xA ͰcYbeNLZ7[R0tjP}_$ |))nщ5b̊\H > ӃwY͒7b^GZ 7ίubhHT dhZ9=\ g?VUwb;QDN(4C7f,&6AwVGV$yuq"ȩVb׊PP"`#eaq6"7J`ܹP58vNBUdιNf4Q/WFPbI$O,l6x3%$V:̵ t֊B+:ަ:PXM;yrKKuI9OD3 d[t 0@fv|lU>s+-+ef3 2 .ҟUKQ _|u*Dy-dK?%"`L! (awUbwamX3ӎ^IQq`v?uGKzpn><1Z1MňT5;.3ZGr7ҢpeڍG ((Xc:_*+φijFb$9( aR2Fw X>/Zr>{Rpx.RI6Cl}:!aAֻV+F\ E%?4TzXAm]DDPIAl]zk7:V5yEc_*Ŏ\ϱΝY M,YM%=?4`۴S~Άso9 +%0'-z/rY4,JܱMdp19+3j[hWAc;W8s郷A)w ʓ56O4J: guGͥөn<4Y+wöˌ}sHm3ۤj#K1ȹZ@d>zd Gȏd3Q?ᲤM~~$8ҝ\d~}j_ُ[(K9B诧@ze 9iK5 h樠CH|NLbH:Ihck>77.6/KMF-ޘB39u05Pb=e :sh,HͬEVOB`W|qo1ڑ.Hnǹ+RrLR7Y4Sܜ ebW}#'Nvs])u…*2XXXuOP*~ 7D8fKC>Twyڲ_y࣢| 9R,^1;rh'qm-_<=CU +nR=wqDaiY4|$鲌pIf*EO냅rf=)LF=|![! 0r+ |8xfF2H=xEP>3 =ㅆot\xMWSH'>c8[4:}ȇyrE){>IXx=u߲4/3KtUÍk)UGJp~66_A6f qRkP'VGv\C4vl}Zy_(%6ѴቪKU%*]wxE~9%Ҫh*~Ty^bvztߧK?Cq);ZՂj_Vuι0n/D~WIhofӓ]ּp)U+x BQI:ˡx/]㷯Vb5^|.^7kbb`&UlFдM92Kh)eƭC:;8%z!*I†(0rUsS<{gplO68Pobg0"yQiB#N/>(#`F*<#l@. AoLm5>r]eC8 H!"fxFúky);rA*D{czp%(@+KqI M_ZH/9ۙcd`` blt1rg摳_ ՈOʦ`Ol|T B(">fؿ&rbWT)qؾP<$*@͸X - c- fo8$8ktƝj2\Rɴ 2vtd@ݼyObOS0uX4Ag)'%m .6Bk&H_⻄#~ooЕ\ Qz/~li \ݮ8y1o~AG <$?\Y8V 3nYa\:Bj(8f m3Zj=kc8MFrV`]o RRK7>Q(q#Ry+xnwktV|Ld`c&p `R0|W`D8ʜ,be?$M_}0It:)mthվP2A/4'✴㇓\Y ];B%x+\Ʉpζܽ>/q:+{,oKYtHbUY|4h̐>x̿{MQx>s 424@_xEjG[Bbʾhҵ$̡Z`ve ؔPP/oun .APZ~H{"ddbx؉KA!yAYCeToU-!)AW{o6Ch4jv1 TE}N[rk*5_*0r&׫ VxGLl\p6Mz .\DH;5N Ot̹e3Vd5|o pR^F&hYNf~f !mP ,-7^5k.qN,@ Ow0E *Z~]v a c\}TצA}iY?ۨڿbDSk-.z ?LR+}{tels|FPW߉kVEϥ77 *Q~()È04=0~qXepP'eKe9&iYo>k~S;z$uZ@j}1X$>Bd!C?#xA2'r5ܨcKx> OxWx`༫;i0WHA: ~+ԔQKJ.BLW32Pѕ<n$當+G-o1uB&Q<+$J̣6bZ6[8lwJB~E|Q̱Q>j9:yt:P*DA3 ,lZ}&lK6StP(mrch׉p{ܼ&;uPMmݪc y|#5t\<(]|zO[7~##̜/]*PC}+)h RVY`0wm ^ `X^"JϑboMru+N0ȱLz"sIEF:}@?vo!Dk4+;>5CC3yWJ<<$bM3oqZ$x#B0&|-&ƌc LP.?]=:%+P>RZzrMUMڠ0}+(`[d=̎ӵ9{&}1q7褿ܟneͅz93":V:m+ٶk`T.xe߼(va%W/̂!zh1&9 y@ a/(lnzɄvץq3)B8ۀO.!=Qmlu B@yG7Qwv`r ,Vk"R4 ^TeC#R9b)o3(F+W_stj il,lP+ƩUGeW&,^}rX]aY?.J_"}zz]<v,<[ 5]P@ceccomp-4.0/docs/images/disasm.webp000066400000000000000000002442641514205130000173250ustar00rootroot00000000000000RIFFHWEBPVP8 Hй*Y>m.G$"!*ܸ enZ-y^v3G溣djOi_*lo?K|_=G rr}X/ݏt?f= ?g/?~OYs_/{O˿dC_~~@y_?~+b'gÿ?Sٟ?g/'/_ɿh!_?uxh~ԯCޟ]G_HoooM_?Kw?05wB?amPс.1Z=П|PKm3j1Lw$9v ݿjP+~_|˥b2:džEI8^sHjMgڛP`iebU|;3Q[g&tmG|51><~P{+UDKNCԽV+R}4T/\P| b? h~HDE?bPq䑬o~Rv3|%48 moǾX=jpyΘڮ9M չKL$> Sždž0?ֶk'<j-Q;$K{X4gj0[jug7qw-\-"u LJx@avS[jv-ms=)-,dM0*qۙ}qJY؅E52C~՗W_-:.!bQz[BP.ʃ,d=  0!U ߅pگ2MRM/q 4- FGFU-V|7ͬBzpΡ 0O*/f6y" {S&TwYo 0"&!^B ?7eY~ fP/K`^˽4ǶmJ fC&8/t"}N! TJl+?O`ۤY GY,0sk\ilR! |Gg%ceF$4=͔MoI|Wu͜qfU | p  l.@7_bƍ,oNSZ/@ҙA26ϩ\g1R!W@霭]r`ZLXqW$VxLO{׾ŤynGw>L6׫cSR=ԩN%yuś0K G`i~#P]Quyʤ]uFSs5?ܡ'}$K.kg | bdct}Xyv\c` p-kR)pI>`V#l2nC{?Tub'ðLG>bVT Nl) ,jOc^P$WS<[r.]FPB^B ?7 *Z / D=c9dJp;)-m%9[Z MJ Tw4P&*ռ|Ct-rIg=v,G Wb1PT Y9N$vVyFCAܰ#ࠄv|~v 0!.mLhLz=8iY )H3;A0Zxhf/Uބuޞ:6cQڃ Ӥ ^"fidZ|xTxK.H8mH3CcAH >)Ҥ\]UtKn2 i}]At.r!=zZʴfQEob;M/(C ep PM3xj\m a`u{Іw0ڹc>BǷQzO_Ϥk&'TjI8{\kcĮ%/{ Ҫ2QjMp }氂$bp)WCL U |eXlyBsTkODX3lV!]+0neNBj Q- n &4͇%3hK'>~U*Pnš\r> {ixL'7¢Sh@Qvwt1gR}RQ_L W\g;X ĚQQR5*}jȍSc*9(f==S1Bc%sL!ŷr<_s%m%rV_rW@/}+^My1%OhZA7J2edDpIO0Z0*dX" aK@+b&8NIqW~$PpF.>IUZ@Q6{m'GtYY`3 lO~WO{ӣ( sreq!`Xm)V93kv"I|I(1m?o VI˦4a*F}xXƯ$2V}\J@gh}FWnh$o g3?D H=.{Y.efLEf jܡ6g~SvgLQ'ay'$~(ź9+q-Bl$kaU#t me?cXO $^~Yv JF^j2Tk>g~-]p2ś/jR@7H,]1@k+׫hc~BD6x}aaIjx_eg Ɯȏnb(r㟧0&>ߦ7ǗrgV~֗߂ůy=lRBɤf׻R4Rhq1Lyh lzNL*wJA3Gg,x`ϟ6#ⵇ]ąoɁ lqẀI]E Ppy탚W2a;ZUBY_{ 03` (%RV-3 G&2.&>UQ;fJ~|_w݂Ĝ!mMP37dx>HFw~F|f?Y~hCO"0U-%_aZV`Q=#SƓ+XčZ4k&it6!. #uksr8%eaO/yl&_pb+E ~uRzkN=F~_񚪚rlv]US ~&u8m2f*Tє9_=?cmA~-ِ#bɅ݇^L݊y/嵢)XHm;5%K)C#ՙ"RՇ{WQ5]ňO1a1Z壪J%7MySׄD!o3{cǡ|sw6Vd L 'uG_6Tڻ0Q(LG.ҌpEOP?}OQyi}O~' ⱶP +`AJ ٫v4| 6T@&}jjSG{[aSl1,?k:Y$5jh h^b6̍ߪڈ&+ h%2\Ͽё _U9r* ?<<AZ>s]5Aт]vڃ?txXcYzgd7!tOLUٰ23dkWF* tum2F{J$|Jb/O^2 $語M1lq=(B3k:_e$(͈ޕgi`ڹF[;; Hi2{Fg7cP%n/"l6Pˊ@E%qriY 8X#&k6CL>H+E~wξ[J6<=丱Fiʿp1S3=an\(ΛςO?C`]}p,FXanߺ8HȓF]:đ©8#%66-~C<1O40yǍŸb |Ċb? OU]^+8\ lZA)7[‡FhqBHM:hH!3d?#]E )j0ūz]E; s]-R!tN;ePr~2Fb0.OF V[<n;?W) C*;OM,ㅨMq-al+f> ffd!3w -nzC c$c!3deh@3AuV RjM{KF;y8% pd_j1e1tG` @j!iєR~'T\bc.Ry,W<+saWf|2襽r; ɡ1,7?ja7MZuU0Z,P[@f bYhЪ!<[b= r%7XFg^QdT(g4?YpZ^VZ]%X8~I[%WRAXW% [[ { }՜y" b\]< 'se(M>/K @`C8Ag{Tݨ>zoO[j%% m?eRk'ȥuܟ~c%[fr/h 𙷭B1dZȗ=.J_ b'0iI[NVxŮk{'ybY8B)k\Cd>^dY?/S_c|: ~.r|2\qwOB9bQ;'TQ ph 6͌/F8@t ev#f]&`|85êdRxK{43*m`Czjbu:Et Adx[d5(=8 ٫G/yOwm_cQ#mgN/ Ds+Q1 ^th,Gj.}yE3ҺCtK}l&X[Т8{N2Ie W*C5j] ?7 qWw.V$lyZGx? FTX ߳k$0b>tl4F9OT5$Fմ"bP{YS|hDyRҲ99tAfFݱS<~X!tlzI-J}U ,]c]Fun~^\/f=Jf- ϟs)hfŕ cb!j%Oh`Y8CzefHYVCw^|@uv_ RXK]Y=fZ״+yZw)v F9ї`fv,3Lک~D^@Uv{M4$ƕ@:RUpP6Qgc$h`)JR%ԸYb f +1 PelI-l˱[&˫]Զoy"qd^G~QkSU<`G 7tWZf 4a~1;;hy Jx59%PVu[AT"g9cBS4QwFៗ4 rLPZsc C1BS,䥳[<`A 2\i 0 @|8QImyh̊7hlpyOax]%}o$Kre=b]:/TQta%4:qF}Z0 dulQ7,v&1ޟOKP6`ÒI?;~]sQ~t8EͶa0|Щn1ᩧ:y8ufnvsFLQ<7?Xmnp;v齓Yef\8QJ%ޓ S$AfZ7(뿮ÐV*:U= DNѩX'l a=9н9Ց@Trc\x=|1w<Ҩ=Sc~d|˗+ؑ n^xNcZ%W '0ʎg6fF>u]2x%M;YPZ#wa- ,'Lz}Yp;/dn8;*Ŋ/ZW;b^AtB. w`?Ŕ YƞXf8xQŨ]XX$"E/l-S'/g[YDv[J݃} M7EIN sbE //]>JT ^uEݬR2J =s^փ)Ya>`^ Gs*;[@ <MgmzvTAOh$eك5Bmx –Qxf?sg.ԃ& 0D0sMS^L>>yF>!( eTLﶴp%ᆁ17N8SFpH)0B`%,EXbiqtLYA$r)ZK>%9Cҹ]MU,LG>z I =:^ K T59dž,R":)Ue㽚j;͡N፯w#IggL#21Htߌ:hlӹ]yӇ d+eOCAZ5EyQ}ZTuw;H,LdnEr=bڤmӋiG{B6Uj\tw]Q\dԌHg%9G?un,wP녃Z4H҇$III-,LdE*Mo/]~*GLq|daT>KԯT'#Owhp*c9s9$o&!:`EMꓼ wjݛu }]=`߼C G,DфgaƤ]e\XHa}Y&t(c'fPNecDQe<9SpKN³UG@A~W1aJ&{gX=3xz5<0%,c'wEag/O(Sc1S  Ρ]TͅizYJPIhǎtsxBk_o֬ @!L_]!oBoʑtej geڱ)xF/An+Zh<{1Q9? dgf!V+ueV` 30*G+aɂ_Ryڕ8'%N-?pSN[_̜tg أ% 6S>@2c[$EfG>较sQQ:Qd{8נDbx ͌. 2" <>}2Ū``EfB*DVB?/8ѕe +2AI嗙'Ev`.EpCF,jDCẕ{|̶c6.(_k 0e'w)32CSp4NX\ztlOm$ B5v}fKTK1֝+u6D07u0"ZxcSDLmy&Dí`8=m߈\v2Sa'b>v+~v^8C.9O9#08h͆M;r]~.)nnΧjL mrXB*;k4ŷ2%ׄʭzvz\>I!~D-fR~3?!4833"xkgyOG2>/)w(um"nEF +$Axqg埽fOo{C%3lWmmAI0}WA*݃^ղlYt>?OB݅!eq[a/ƺ`ubKonSb^l ]FY̨1Դ5mF㟠%?>\^BXdt3{v¼AH@Ϫ;rcpB75>\-#|(_Ұ)?nJ@L;AbN+Hu|wFclgd)˾ZS+5>T)wj "VL9rpJ4覌<-\>K+`rn99hyV4,>7ÖDyb 5ǖ  X zg1צ漾+mNB!14Z>,= U5/hO'/-C*RȹqՎ |B|uVE;dSe7nwm̒܄U d=i²&2$?#]Ht+X_GEgmzL+.7H z%;8Akښ {پLƎmbqo 7Q iT˾AFqvNN&)䍛=ZB;S`P/LpbGR\G'loWOK_yV?$z &0ziR/LtV :ό^SQ:ݻjXeù2 ̬G͑Q}IilA bh~?nǭ!*T#n>6a>a0p hZ6ƞ{Dڳ뽯_D[e6SۼRTNJŰ 2<^SA3b0\48NL^EMUT̵_/q!Ӄ{[GO:/&y{'ѯhG:ԄrZɹ{*kA ;~..no GpRi^U& M2LMkJuAũ[}BzqwY޻Yj}Fo-s@2&vH8UY Ty#HPOaװqub8rf8Cģ1&Vy*p|šEw^fa+#߫hl} Uhr6$I@Ou#$FAȡ9Œ6ѓM xT f|a!5Q0Y_T8 Ɋ,>Yu 8#ߞǀ])KUOd X)Sl^ BSۊ$r$2ڡ9))%@^ dq w L y "LO{0Pf7x>oy] LUtʜgsq7;~½,0rsPzO_y)ig; ֧onZ sj_k =)bھƠ@ĝE˖0oT[K2LcNj5R(åx_ 60G:7UHN8snFtk԰!:t w+iؘzYhU=~k=dzfž|]!tqM DTi\FKZ# U{!}VڰNW-Bݟ˷ޤ.\|PGF%QLŇMQ'n"n0+~j6[V)ε䇦}uuFdgj0_7`layGMXbOo&Bj_:8HE^m%xlܽA0LRH$X%G!R}H*eQQ:G&ٌ̯?@aN ז-j٪W? QR.'g~IgK/33/ 6x!}G0ȫ?-l%Qq'!Kz԰ % &?\ v!R48Utz+['0~!.L/KuIעDZ }ygadȹDLlzV5Z~k Bq |J ( mS UϪ={y{oJqHP8QJNxF{Jaߺalrn 6cۤ)W6հM4b?%H{< yn1gl "ĆC33_e7U(A5,-E;_u_(o1-w"ym9ĦO5Wj ^/(d !͓.T?_<M-bԋƆ{lpl"5NLnbc) !߇UjJE/^f͢ ]Go\%vidz2oYUzϑ8TH8C2 DԌH? FX˅FQ$yE…LTW->' [=8V&f$cWqOcvS}%DjgXl  Ja2 w\诋'|OY alpH\H3Jl7'ovlw##A%醿)G6\7\Rm) -*[QWASR-3s)MN5KťKR6%b|"~EEqRSC;sl DD%/'l2Uz&!5#l&SiA-H/cD~1Eh v_CKF-2uA2 "iJO*ă%oHm&. *GVr`>Oϻ0%Jޥ`~nT/jLՔ'îF8B\J.c~:9|WVߏ'Uc7SW+8~reߖ|%I2AJm@utOF\ljXe?ιP}Jd; )$܎_*)C?E҆ҭzF2=DWS`C[f*o/B'>6YEQila}>Z`BxV-),[gEb[G6R0gXduJXQ7+j6*gԝQQr-WO֟I/ \.YA[\|84[Ghң;&գ~w2!VtQ=c? d]&lq39|;G?щ#D4W%2abq9M>sÈ9/l5ԭ2i g9~Ϋ$!kK/X@7ә{J0(O*?)}LI,fvx?D" QK7Mc33՟EkƕNh\~S$ 1!f꼣W8Dke4)T(F/==1kFQX9rEEdXe;G8'y>Z/=栒U$Ჯv/W\\3lC\Y{ehĈ&GbA 5o?A r |=-YC}~be%R;Jw25ӱmf}͕IW,eܤ`3Qz\4\ZǿQE ^;R?E޼Fh5UXYג\%Jo]%} <Q*js(~0sFgn9X.;*{;bVtMn=id32_&vϑ0}r㸞-`U0V>7Msn$~Ybg v=bDL ?lcFņH!z7)hEAJCpqjZV5pjwQSǵ)FOif@m)NU0 Rv^$ȧN,*R.xGr5*Dfu &4yb`?!A}$!9IC'N`X'<хL"xOF1||Of:$ت&'ItnFc~}Qʃ(ϵ~.=,QNJw_$ELG)|k1L/Bdg^N"Ui^oUJly[b]}տb6EC:zd^~EMRr!ةMЈPLN<,^zb%#*'9BVWYÀ& $FU'#2p=ji4`N C!0kU,ĚM3xu XDǟM,[p;hKŶ{6VJΊV*`dL^h1 PW~"/\uѬ#OUQtaϱk b8*"3~-S^795Ŋ(u&V8CIviEly[кS~IlQ6N iO? FlxtgFQH Rש)!x2J!~O䭚.0 z!OgC1G(~} SrL.q/:?QLip0E-v%ͥSK%3l8}- :w '_ q֥m  ϭ#`S󉴍8%N38fa=hҹׅD|zrWk?C.DE~"@nAV^v6Ay_t)JajD> |da5(GjYY+ޖ3,) K+g{G.R +7I˘qޥ\ہ.&ElY~F$gu< &}Xc9g&N|$] JRb*Za r(?4Lgj6tf삌TG_}.0ُp,UBm5:e;H?^I|Rsyeۛќс㲑K Due[B7.8KZ5 cC2I]X'E(x{CraF,mm5+A{>X9z%Cה 0d6smf2넇s'N(|H0ꛊB48՚xȋ^"K}9.lH)JpI&4\︧eYsᬘ`QvcMӓ|ÖG{ҵQq|ֱNG9 3)<;1{J%c4j>PC*6qs^I8:AC<<;b9UGB_>е!U }X@;3Oc8d,jDx&$ EΎrh/f% !4}>Yo "WN/fg߲Dhc8 x;- G_;W/y;m\NXx':<|TLw׮RNaT5("X(bE\̮\Yt=mv8}X'0Ɔ]xqC((Tx G]։juuA:"|&} -^˞U0!sew;2Ȋxdt%=W䇝6ȍ(!\dPD$B^Z&Xb>Y&ןZ Y$ IO`8Qw^M$.j ݞL/W<'v_/7Ɖ\}$lړ쯄u^:zdbU R`>l"5= p$:zbO{"Q-W.(E|g)y ci+S8WӰBwݞ L$<nx7ӄ@xgSBu;і}B&U}xFPa 'ղnL0\_n]`,/BM!(;ig3N0|J#驞GtYHr9}C1F ٵ;;s\~n7)4 ~HL|*(MʺT+@$a srj53oV_}&>6({f)O*'p`zNZhKm j^{Ik %dަL=TtBd?>>Z>Xԏ1{د/"64]amL7CYVM \&5rţh*5_IQvhBg'!*)Ӹ/E{u=IErvФ `cseN0C'PÙfY8X(XL7OGtv8R)ȗʓFKΛ*+W[\-X2mۙwzyb<ɡLۂ$ rI$.!]K˜@$q,,2taj+˙एZJR7EpWdں3E3lNQXjR pOO*|4 ?d%hUmI Rew£u^`lgΊ >o58O(a(i.z4iW)\>FH:Lè(ij+^~`"Ayr'vghEΙPG0ZC q]' aAqkS{~ GxNՖve %2wU#1MT&%.ňQ_1L{ԐPyyJwKe8!_k[F7ΥeMn9}Rk@u0!귘CCù3) ]TWJʜrDe aLv+ xzP qѨ!/eB5lB8H\ǽR l%#Q$TEj!A$续ojh'hjk.PMf8! u 9'Bzq-W[gПnCƫ/A x$87t#MK2y)8YNmjPo0 ބ:CfoK&, d9zŲv*,I2m!fhZ;\Eehff[9A<7QI8MXH<<  ٦}CYhG5k HX`,`= ̱~mH ЅPz&иF(ZHq3T#ɅtJK$hܡsyBݫn&K.jsxMC !pOj.?߃@!Udc1E@,7^fr HD 'Ć"qN91-5/ i$t|ʩqDTH_ԩG ΓsÑtB)~ҁ!zŬQtyݫį9-Q/- +5by|:`-kRE7 Dr^QbB7q(v )3S)+-ǁFɷb6xm3D\_dYÖmMt R6*c*}aJ͊?ؒI/346XcuUo;z{^;-WѶטřW@,KjMJ2L< &R14Xa"h!ap9WN6gbaMB!麍x"]$X땹L2 :YKXTN#gkd!Cg4I]8l5&gL+Bm/XBx)-;E=JHN2z{bj r(Nt&TL xS0ʔUIGGt~ZYM!(%[šgtx|^G~?5 ߯*O^ۤyy[8^9ʼYՀ&֌ 6h#l ,L@@' ,>-r [y'rhO1h jcqS¡gy+T^OPuZћ}o8VhnӼKN] IWҒ~jٜц(=:/J&>3йq^sz bu:Aerg;:/-IRj\UyU%=YTIv:)G$4D;E -Es\8|ܛ@mW"dW \} D2zJUaڜ!_$R6P8r M7{C][?9 k7@6@!b1_XyG-swvgʲVɂ> ϺK 4-Y~ÜB%Yϟ+2&h6Ð^ӓy%7NǦ6<y,r1}ky7{ ϧiOכ(ZL @~[m+IFT|#MK'ύRj&rhS7,%B4q( nN#<Z (q7ET9Nծ>iTRflfȭ W{4d;9A=AQ({^]JѦHxrsV]Փx hqMΥD7I!h(Ma;v ;>4Ѫb[n8{S4dg~h} )og^4cQlޚkDgWt]$"CԱ}2}$YRtFh6)mgwZ$L=I- H.? 5+x+zBB$Tg!h`^ KӅw{=65t^`Qs޴f${L]p?\bi$ -l#vu<[֨Rr %F%\ 4uz3 @geq[+R8|_joYz aCw z',~M#vij((kx?D:*n!W΄Nޛ= 38QҺn^15TՊTUɯ4JPB gH+9K6Ur6BE|].l̜ah[-qʦPhxM_|bJ"S|ժ'[ikGL4ś Xyoڥ [ tpJI?.`+86,EoX|qy_FP[vc3R"L˅zLB7f3~Yܬ2Pe$p6P-m;DV}0CP(QqiEJ!Y ;86גB~C#k7=(+=|[ֺ.l 1Μl~K"f1:ڻ_("<.Ō+:~;x#umjV) wլBn>:\>,Rm;&YiM0Ts&{8^ʾ ! JKSd"fW8 @ňdoB}uk$O$T2KVw!LGE9ffI ؘMC([k_d:!ߦuiTŭbϴR 3?K 0M{`Q7O POw[Esʅ:}/e=_HUNrn&w}T1NQۜpilb(VI0{;qbz3PH"%_Zl6gݫyErP h!7\aeJKޖ= Y BdUss:蠸b,ZoAK|;<1 '-p]eK(_?Qq~^y]BuNINrm¯džcvǻ:؎dTsoG N.3w3\ }\F׾мX;[_ `Fژt's̑4;r.ޅOg?Cl2I)%}0z, m~jQk' x|$߈F6s-@3XgMl ]ʌ}˥eﰗ[%_ld5`ՌÝZV  s|2`hƀ"G: [T#Fh=*++,kKEI-H<@} )R>G<^#J2!b(]5X=']/ ,k#Io!^nσ", eGAp<3\b CZDI+bl0.JVc9|!`WnjeEUY D?K/ꛄ #)TYe#d/u'~6yTbH>4,^7?%l?o8zTya%Cw TgVͪO.CN3CUR%WJܘj"-* Z<UVqwT7;xRvk#^qSaA)ʵ}1RjײyIȦMD" =U `Oc֒gK+C!~6(KqtԎ穣XUvv#n#\WY|8XeU P?Zc^F%g"ocj%3 E;Ѧ1ɚI)IeK|&^^d-P`DL"{;rJSK2rۜL(a s@eF˕Ӥ[*-bh-^G@ާ3#?4tɹ1B+z3F1ն8OЩ8bKiI]4G+xO3,(W#(G3o<Sms,\@1@?]-R w`}=-5m30lYKLAc~U;Z[ū0/ X`5bYF8}Sa\{Ž )7S5^q&p$9 RعkбFɯ4Wa0Sh/6A$JM*`ZoKQ,&6KXLEfl \- [Wpx;R%)0p%6mΛVWW¦0!"H܃lNGμiR`ʶC+?MM|+*=z5."aR6qQ\;c֔[&]2 23akfsbo>29FoY+V:pSU ^3PU[fagھML{9/l̵Nu@8$lмB2V[!<OW|4#ڂ_4 2+3_1oZT#0atsA*uA)J1i-]a^lpל0dz (?ʻ/nB-^5O,)ZFwNX-a%M9e !W'0ƱkNewP9H[S@))C5S6OHcnf1 Vߵ*ʓ۟mLb!"SNJP!zl^Nje_{B +aVwI qYs+Gw2W9Q80-_X ݇#v1rL_`l7 ;2YM D& vjҥ!nFd2 E!`IzF2q58] RgLЃC- -B'(`1U"ҊB)@&*uXx0*S]}+hMgg8H,q`XUyAl:P$49k\+FxTb<ȼ6T7ɾVE .VDoRv<&Q'tƲNfXGEwUw>d9p$~$J2g%ۘ.:J̗' N;%Sp7e.?h&Nϱ,S[9XdHf䴯,Zm禀yocJ+lEMuMPpfpl-{08TiĘz}\V9hz$N0]V>9vgSsɤF+䀄7t'N{0ew ae_Yeɜk]| nqjQOT(Lɇ3tȂS un}wŢd9[ݢ2c )gELz(begMStBJb?XIQ [}(Ec=x=UR5?`_P19o]9ݥ.$˅q00Ґ>?ѹ cnk[A+A3|XB5uNg} nC{,j LO[3<2 U#9!htx\2(xJ8MTre&+tD(&b(؅{t U0?XPSHpA4Ly 9&ɍC)T<|/?drג<|G6nXT RCc@I6ֺTZ]缋#aVV <0Bè2{(NlPhr>rhSRAyST>D1@= ~@)RJیB^JiĚH^\G[i-ߎIFc e#N".!%Xb 1$G |4 p+,@"LaFE\bEɇQ>)>m`y,W>5%lhLXų8*ݥ57ŋƔi0KQjFQ  {[dTDUcS7[(4vWe'W%TfQE&Dخa܈WQ.H͟=TB(g]869_|'{ Ȑν!:|,"fb5G3W[$׳I>o)^bqX/{[דbƾ_T9k3\D6{;I);)? WSWw>!=0ր*o'Z*>-u4 C @19\{A } L,k(o0_a? YDu *mg&*XvpJˆۄ ʀ 9]pKS4bd8X ۳ךvH07o%~>Ktop ŕK.+G2N wwsR$rtƬQiUrەXb0exS>8^& \dV;j0aB@mEQNgf w:*G=K~QދRת8erk0;r( sB\ jz_$H1 #Q<z Z6H)ocqB੽)Oe?pHDc+q4s^vp n\~`Tkp\?7CnKxީ=",5&X`\Ik=&m3(gxMτfyc-ZHsk&7Sn~Znڇ%RG֡̀+SCK[- xVy)Hsi{gq[-]мtO:d]]M !,+c6.q_6qݰYXp`~'.=/3gښ5P0ۢ-`IğcN}e-ûDa /~8>)O< xasBch@MB/F~ vZh#yvwI\w_RzpڝK * : Wq4Rz?&"BMOf-f 14276H=Z1-\hW}tmaUǥ6ghбo'9GD`8hJ I% 1^:v$]j+[>NQHĺy-b3f^mMڶ[̠NvaoƖWRČ&(FAr}!(/CHH쭪b^RUoITF֗DX16@pke/8:S8̙7bJQ8~k,~9̽q%03tkXn2# ;9N:8d]oVi<Ɉ0v*uȈ7Gb&텪x~R=J97eUQ”]̎E700Ll/j W=IW0ẈRwH?ڒQӘEu4ha&uo;2HtPs00ޟ'<( * 4-GXhe»M%3}f+qJN/|AshȚ-Hpa~m\;k;2Χ~M"丑k9I|B^ ԾR| '$ID3 l7M3^nChg v,󆎍 nITδ +kl#A I*ݛ2o.,ǰt 6eE Z OaOAth>xAH_Hpf ?61! 2e(ߖ#AغS"aȵ߅Cq 4L:S]oj 2_ߗAHXiZS0iD=EgYØV=|M_xU*Y#9_%cBlc_#Pg,;]`<7C@"/˯j?)dvv<L\ԍȰ|aZ둸dbk,TVT۞/IΜS9Q9H?t\>^XxX/6MQء耡u2e>rcͅ\]E𔏵 $#FszI?C^> p`26BXQ2?z&6+׬tn6kRO  !7J(B/YoELI[F<~]w'+\4ꑃӬLp#4?5QOոb \ʶ2:MI;R=NVu"#_>}n0;GrCxD# F!]20;lػ{%@>JeU 7+*.iiMN'Vidjjqa|^BKW6%""y7g!Z Ed:BR++,=8׫zܙ/ȵMaŎmcG~Rb߂ BT q@)9{( :V#e[|^s]s5 ՒIFZ%:)a Q.f0c gr@1&ç]}Ah,jwpWUcWiF$qqBv TCE(Q'kMi`/&F[.O"(t 븠x%H\FgOCbD$rR|sl3ʷ@_P}&?Epy["#o6jlF5%Ó(]8]&R]1W v”[՞PC{06Z*!F/ׁ8(@$HR:~pԠ bGt>[~BKh? i-EN ȝVCxKT[4(LiCzI]1T!Ͻ܊=* w4خWcl/@դ+03}e0ْ(LeN 7鲺=>wql.Bk[Ð 0}@(KS_MgcUSqߺ<)8[&? S9,lk'ȿPg2kGy!X8R~ ʘ$x=D6כӡ[ K1Rm<".}f:j\ jcIx<'Hnt4l_rȐă% c?%ʊ@Bɗ>cI{s1-͒4dH*ojWzþ_!_s]:0;^<9>r (֘dIi7uO" .^S/ȽK @N \ci&y W)_+=H݅9}es1tnL8қsnŧ 4he,dv16I6갲{#&o5vvwD*BَI@|Fhy$` `9{s@lf$tMa"= .f>\h=$-Vmg9TV">\ă:OLsp9zTS֠{J,?[{h=gmsFgx@YrÃN bPOǀ<nsgW)uGrjEXmFx/)G4D. }ɬ TSB& @WUD\oQa^tI9V_{pҼjZ6o՚B8:$FhJ?m̢~  ,5(c ,{.rG \pCP-(lړ3SƖwyW׌Z:TAT]mtti(S5G+{,'gsgdpB`4iiYʹT! ֲ L11??Dzu^G+g)r42(^>ҥEwU:90W5>[c PƜ" _ӥ>n%aaB- |/WT&Pdt;X35Ny@a$WGr8C'ϊppRX}~-D.:N‡M +Jscq!zVY%͸ `}odxn)TQ|Q;Ca#M4ca]jsDkaԚ An(jpmcx 9yџ6鐨'b:f/C3N ~huvZPQ?4$DK^h"mmc5L;|*w&Pu|C QmA~15࿳z 6(=L"wG-7oc`EaNhS޵*̎ѳ4kZ@^|qq[w_ـ0\.D~ ;SXLW{a`AF"H*xhCZq5ZxpRL>|nDߠ4 H,vC>eQ*"ͮ@dq,=9w-$8=xak<*:;vw[t$axMϻ>B1꾕,r9Q)?P+XkkVc^7k HAXDhaܗp2x$]'-raTJp)}%,؍Au07Ud*V!mp@`L$+cg| ,^'l~8Sʾ97n)Mk-[j^}2XC ָL^/6.H :^I%?BaYzH_OϣB\tVE=2~ c ÁƏ =u|T Kp?N0<|1RV'NZB5Fd?Dž|8Ws\"=bwziI)4 ,q!Ug*bΕ5ةv'%0x؏sS1-%:ypޛA~'Ԩ]?WK6C0cinQᔕBj7HUx;$V(cnƍ5AI@ d=h$ŝ9:K%*2|YN(9 tM~t5M&1]\(|M"N8ɗIrއgSAXEy%V#wh`6ĵ9e-ԕ߆y<Dtb+}s zaF=&=%;Dog YzU0X刌Ƅxdw$oD/㘮rΊ,yL8/w$D~ArC *BJ~vgo듟S\L^l9]XUIsW5'Epy`ɬ*xXnZlV/:]I Qjg*GkѨ;lVUʳ9$6)$0u8>cS9DK)bh ۧNe\ r(Kt̀Iu#]l rMGXGЗg* zڃ#+5׈J.~XrHa_!Zs"9?sVEH4y'}7 ^8,tV-fQ6nukӠml:tŋ ->@Jʲ?":|MDhq =]a^xt_$h*Zq [r3ZBm+#Vn1 ՗:@VZSTC^B&a߭p? /[< >TN#Q3"7Lϋ ~ECPaLCZz>n=Dv^&Je54T7+SKN*?[ cnpaO"܆[Va w5p4 #[Pl58D:zjLIsaOpj0R~o#.{y~*MhCrqE v 2xJXthB9T̎m@/EKisDBq<>Ni Nɝy~c`bFb @/1Yw%4*ϫPN\೿l oYǡ ;B2}FFfāzj,D,qĄp3OIGi墣8ih+Pq0lۤo0 y4ë0 ~Kvm+10p%_J[Jd+c+^CYw@ԍv<#쥔w7gT_rqE L"2 eNR=bN]JT2L WMK3~ ěVDaSRfJvJ"LJTT HWL8s;`?WΫm^ϕauЄ٧)DPš)է:0ϔK >,r2t0¼iaWFP htBUa&W ,/lCֲm՟r6Wr@22KEj7hp}o_JBòAGX,I}'`"Ay@.E\a/Kxȉ1oj sv$h\'#>"U|錕Pș+@z6 Wzabi6PhS*{_ M b.uN쇘2@Vn9s=I=q#*[ЅH,ߛ|t!4_B`[ ѿ ͺc!,cVC1.=vx0CU_ yZ7AMNRd RqpHI Aӆg ^ ZIU 6 U/4g']l'm[Wn@|@kr!rH~ChUhW//ʣ yvzoU|*:Z]I0u>\/O+}0lڛ0!mXx'0L=}2"-}0[! ijƇ~{6{/놋0<蜧Rh1͙omA`GH PiT]`4Mח3&7XZCH0I+wűs̔Fq(Pd܊͌H`ҝ@3~njxg䘢!k\zUdݧזL[/(P=X>Is*4qAeTjm}$EeBDETQ1LRϽf3fL["p$ 茳}o|9EVPṎAٞ(=ks,lŃCxxA:3UNCr=AߥRh 4yy4˔8*}U [{/~k>xkŕ2Js7wOaaf=xHD̡3 Xakda_9,}'Q3 tfZBĤwUj OK e Sn(U[gOi`$%nO{1bFy3vm h+1!V4 Eky~K?_"m%ցoj*ort0ՅGoJNǂ}>!njbQ}WtA(9؉p?>6 #b:ZrIxbf'@ioXqCqLZ=t 6\{3 Yo{(n}|Y4FJ:6j1ېKWH ] >B&Rct0eBpA+#Mm $mCD ;bqL!#{91j35HͲrxd_AId+|Cj,a!)00hW/_wlF! *6c=;{L("&,UKg/WSl]lѬSũiS r>Ǡ@P7B gJu~'E la~5S"@?!Ht@AQĞVu%>P&F<ྌ\ZWkrwh_w)P:}l~FG*\'X2gEO!B {cu?`4k|>>.T8d‡tsC"+=:pcI#v_?8tW{Rbxx!u<ߖщjtuR+f3gYi Y3 ah-nu#pa esͨvV,ohuT}>ō5u],SrO2`X>Z|ߛgH!Xw«ҵ1S;>}BUS~,)Txbp.*s9kbdpBnֱ77+M,WO8q n@-QFYϋLs@ؚ#[_V(LP$MaI)G{Î[ZW Rmy=$ea _2ɧr3qQ$ a٧*?v:-R|AJ&,,"nҦd# ɳyLݢڱ~V)K[ 2;fL>>gVDx?.UzQƣG:ި#;uvUu_PԠIHfC"IJ#ۆ@w+g<:Z̎G uLxK1VÜ|j\ ^I("8(<ǥO%ZxP6`u;#>Iψc]u= -";ZJҬ|N{d%羮p<-j8&Hr <%wib.gl FKԸi!MR ro7Qt  (W-=#K:~E\opJD~ܼ;&dޏ\*r8Hz1<눸ގ \tm[К/hn>"UJ}` <]L#YKz7h«ޤ$a,;Bo h=eQ?^Bz ߯]6# 3oax#0/eB6xRj ?E, 屷6'F:]78%{ABktc;'׭vҽYd$wjզg^HFn"f~Ϝ/(Q ^H/:(֢tN_% 7212osr)YBD~ XƻňH-6|~ Dz wո&¨=|_qTfvkUI exRz"_͡fjLs=u`mYv[v IJIa1ށ΋SX0X 4zv$cP6]&Q Hsp=Ď_AL=}/Gl9(2a= .ښC69E-оvj}8Qg+mfDH1Z.3?k;328 ?kFl/??l̵kw5yG߃"S2^_}b׊.$~D˜\ 4!UQ^eo)d~QeM 6RSBMx E?%TP5l([ؗGTg0_Z5K& !E.o_+ٜS\ZTsM? r'ysR2ΰYݓ/'jqG 2wtqPmm].1ށ[LƤk_뷀%) fGE7]\9zPjrf0 ɨ4v&D5p9'PIm$S zNyvp̧'Adx_F̂f쓦ODE`,s_y]q3,ȯfH Dѵ+);v#`y:)34 BOTI4nےH\ԗ̖̽Q;XyTn.TpxoU1l)>VVț͆>z{떵#cZy[d?;L{R IKG@t,vOC U̎e 8]UH8~j: }I%O&r(ּq1׳v`c|X5ii)F]g=} 㵕V.z!,d=՞!Gү}]%Fl>Dv~ElZ' .rǭ{)`.Op҉R]1ljW ] YZ1Iy]mB,}\Ӣ]Gos.GLVw6e=39oiDyiAO-'$#D2X[8d,X\] v6l*v@Cλ%cή,bXs7YT}Ao+A^ƄoCC,Fan_1E@޿쵞Jf`v C{yxwƃ3Q脇$]s5xcer-g >r+eYF J-f{ l/c"+v١kaW^h++{Q-!:|æ$rqªʛ=\z0m$R"d$7h:60BɫwwНl#'ԨQqKriLO81e)B$¯dǾ\E% )5ќP+C<>[4Fp&F;$KvrAF+ JUÃea8JN~_0?G`EO@Pޒ:$H ;A0Qλ979##ZߵcCq d\Qt_sU*cl`q0cPSڸnz-lbYf{n.FFt@4JqBdo-eVڼΙ]VME<FD*:Y)| @iߌ*(]'Q]u@ ,S ȡj3ϞQ]4p%Wcڕw &Ўkn3q8` iasKJ&xog<~/s Hb=FY-~'a"NBvA1uP؊[,}4U!s< ȥxsybh;טrlv0#aKTL_:@ȃi=RkJ!|eOgjy<1/=0*j:^W-҉۸)0є J!| RAGwy|P~1L ^YųLПA| ~s2GTA(uMhj ̍3LuF t%d lBj{R27bn@ BD3ĢXgu ͩ}]BT7vJkj9 `ld:x]$QY5Cy#d=Jb)[G1-x1pj.!* "u}Ơi4 FDK~Coʒpvu`N3lfi~ )ԇ/ƜQ}}9y$KH~0>ijD󑤟|uIY󀇗˟7 [jB| /Yc%hAYk.c]0CO}e̢iSw@sG?,wu8B˴qɱ* y?4' ;mq88~m LE', ^mL㍿貋ɘҎ[etXNxC\0/ q( u>?=Eي#s/ijӣ sGz`M7'\cJ,ͭ崻9Saa8_7<}c| _uiĉ$Pl/<9ꏦV" _ G}3H 5.Of>w);h=ׂ9"A,|wo# HFwՓKЈ _/6.Pt9ZFaF'GZm](쌨(P;XIhjL{ҥqCt~VprۊK*x3:Bngy%6(R~CXZϝ DA|=c/O&aJC][!=IZÑFۮmc/* ݚ&FҤg-)yF)5rB/ eUTYW5^-KhCU1k )T+?$aZ5jc2W0Q c<58Zl@斖-6ϊ$Cf<.X-I*l<]i#ÿy9W1R1PDloLb&M!]; W盤8>2BM6ܴ0^m 誒z* ɂk-ΝbIw>UكjwƎYԕW*R8@\(4N;aXs7vYRCh(xI~yt##iV=.T6;M9aA.7{gldncޱ(RLJgj/-T~,:ϟ>Omd|`cٙM}E ۧryכXen?R VdL!hPQv>&~^+r ] =2Mfr ʚ&4NUO!^_D.V틛ن\Xat/pHiT׎)` _a{'DC r`ꕐ:pW0cw|j-,QCEXѕ[RQ݆.N,%m5E7RڨTU՘x1P|/R~$S@a-Nd'c B׀Y?J ;IP1B} Pu!aL,RC} Zb+T3`:Mrr$ c⦼T KX|y{㰹"w( bҜicfz1rsj72~=S9 QBYHW;n.'_yh`={헌{3^'@0垄?K2)Q? HAL,% ph <^;>uG\eQ3t9䬬TNv5 W={|wM=*8CD 5DS Y=RN1LV*'[$Nua/OYf5_US/&:,Jol{>DS|ٮ0=S+2]rdˁJ|P&$_K:u\A j0:s gNv+l˄HРH!TƓu_bo."u@0@gߡaL~JrxݱE& @4҉b]yB{{yT`SFvLag0 `"Ke$bY蚃WvN͡@wV$P^. "n~ jM&%ѵ[R+&PH8}]=Ul~) .z>iڛiyJ=S2sv )tB~>x5n-m"oRh[ċnj32$L@e<Ntk%~J 2=xDW~OrN< ȗm!Lf1c >xqn꜈w[.E"` ۀqF GQ{ni&ˁo|˜SummTxPz:9>sR/5v˳^ur^9ƹ*}>b"C%zߛD3S;ԉn5` p)BzL9PmV,OϹ?@TY: y;o[ pqXϓYKc(m-%2VֹȝS`۴=q}7vɶZ C] dYhtU L\3{y&|ǵa,cꝐJ>W/ Sq]x~$M烵@jZ{M9ETj՝JUl1ٸg ,ְZ'Tz69p)Z:a8-mxokƴMc-:bfbf߸[J'M|0Ϸ3:u7k>؅&Ҹ [0nbQ g[m䘨8fA&VL13\Je:3tW 9m7 +glڀ~KQ]z40|-ч#kǒhJFݢ,{HPT1dT"׼?S >d=Q=pegbi{@2P>uMLQ7*?@827Qk Z+rr(.Ģ3}qi%'p[AgT ʒ gp؀3|}i.*ЬfPT `zH],<%NJmtK?*s!m=J2ߌrp:z/.ټ+uaY~j嚁R>:Zu,v2y/Z=U4.ۭ$W!}0Ҙ&"Բne C i=: 8}Ġ%BZ+M˴K$]6_"]$=L n720jI4Z|vj3OEe&p}wKZ@@EwyO^@8WD *D[$"-|wp<1a°ULYzH,YLy:ϥ/t0DSyF'[=qJV;+l'H[SyA)/g`=sXv ?L$&"33o`!HXZVPtv09oK.f/*?"U'y!2ڎ]rq2A8(#Ϭ\AΘvS48Kɨ7[ @T̲Y?pAH<)+TmbqvS SLGV ^tq-9aPVCv|-iWp-y.3M:Sɢl/d T1@[ƆZStZnezpgVE,9c{bSR:kRVr_ $ðwqlB22ū''!bHNOL.4*Φ!xޮS4j s yXs %K9vlA1,j<F!v鈰汥`WtV\׉8p-`ϟgKcfZy-da#Wxjkч-2R XNƥ>Q1;45 FއtEP-"mNQ ^C< z"4qbdZ&Z|^k:+MFؖ,Gw*;1+~⛭?>z0l,.ޚݧ GjaV}x1QũbXyz\'>U~$i_ѪV 3[zMtH/'hm{\/5'gx<'d ֞m <l):&1&5i[d'9ybh]0|#q@GWk^X2{%*{/S1 űP9*xܘ*.SB=ؔ,K=.gjm컖?m> ۀ!N+ȋz[, #7yǘ'HO ,rC٪γQf芖%[R(Ecm1)SQ@#cE]F̕|ȽYT8-ܯe y⍬d@k_=8w2sĊF;VlE-Iw};0+,*ld|,P{[% k{&߀3oUā׉љ ~ ,VCnřsBCM`d_v9+"9FK7Qe\t׃Iz'>HynM;g*_6 ^TK h`Jތ:+^ $juF-Agji!C ;ErlY&]O-Q1bJz0 >ٓl9FdaXEr@]$TN ʈ#M+;DXQ8kcn?ʼ>=&OAcAQ 29tE5e!Q@ڴXYwU:|؄ v\~, ~',އ6-*;5R&E^#veY/9TdAS9\16[˽ĶkVao=( ~34M ꒀX: 4d|2XV(E=3g4<9JVEM XYҠ}/TA6'`h(ɡ#zY liڐ'i(sJd[CWShpҧ=zShT1j^?zL͐Iw 2ֲLyBvkP)ľ2 쿁S&au0-u#1]y7ULC^|^FW " Z6tÀj>ɍ>9Z,Ls-Hz֮=MDS+wۘg$8n^nb8RP[ZIl3q~e:VQϛWTZ"3{'`dSC(Fq,G?`,)V;zѱT: mvk'\fACFvӢ?6Jfq{3U:;:kgRec*C>L˸]aoO9A(AmZvsoE9 "}]7ّ.^osnLiX-p4J%Sm]h]4Q}r\|]`+W'"7Ɉ[|*ےD1^i?9Cd1` Al0N:  >r=qPU;ݯ]UvI6y=NT/UM w KFSFQSh&s  պ;YB-҇m^:.x@T~&K%a%_&_N6QӵH~G2\rFPs]"[$[? 9,ݦ|yCX)NHDwc $N.,@pqWBFYtE. 6O0X97UA7]6dyD;`kfYu(pS5"J>KؗwPc^*E'dA܀\A͡#)!G8k*BeZ6S6 "%J?Rh~U;lM-ߺqf.vI\|eZݾm@*]FP@k#> @?v:H0E$ɳB"贈!]yQi}{6ezF3J}0v,;yE*uu 'F+7sDYv,Q^ɏJ Gm;ɆQ|}vG`MP3^FKHJVZ$)`0mPN_UƵ7p9nB1g5hfKFMscgNkgF}L|]"FZq|32&cbtgve F6jc$ݱo)QTV!.*_){aE!2YQna<9)t5!=ؼ7̽g3K #wd9` ]:{HVZ/BS)[Ru4ΗcLCMKq)Vhlɇ=G|kh ;dJ(p '½/ ܠ)(dJh82 +7{:o^;y}n\Z֯^ }1DtID#TΗ MLz.?:[0ݲ| !Bl{}*^|R-gsK⢻q8L~] Vsx̐vU[CK,JS 0 >j__BWWRu5j"mt\Sf Q!9V*Q$\ٟ'j6;&O ك}+b;i00W? hϜV+;:, bg=R2n=pYKLUKF#b ky>+=4<`&x uS-W- 3{z#"rFwj*SPsx6>~3ٿL Bcu2C|QX*hsY|:U<|qa ^b U1a$dʛkEv;>GV/oK;8UFS`hMCvuF}ɢLHc@iqZF;NGGI]D$ <;%˷aI,` 'A4+-o@p0E/e5rH&ˮ+/Ev-)6 %^ 7!ug6ٮlaO`)/kO^֞È1l/?c#NIIbAi8Rl?~wBs8h& Z@v5BQri,C5/Dy'=NI}uTڰTV4 OzY6ϾpV7`٬p F]T8``ŵt=1n?GĊx8C]7KȻ}wh_hUK/0ы =33Aw^]l=gk &HfY26Ȧ6.XLub{p%iF9(\GQƗXg˔Ѻ[Fu G:J 6j(xJ@vy h*T!&,ëȼo9za:0 >P.)/T8ȇkE~Ǜ 5H$B3|aOa*DӶ( !VO* vb>m|/Ws } YX4 X`%G4e/`}ϒ|0Gݘc[E, HY~0𮟣 LC$F8X[$V!=BٟI{5HH϶xN5cr[V8sko_OQRCHӴqB9iωC|Y\NDIU|#=ߢz3n"mݒ[ E(w)G0Eݷ њg+J8COn9:UKt}!_gN^< ҋ3]oT^Ku: D/Ê]$ssFGS/Bd[EpU% Qh_4lL'r]yQۅENyGx+e~YC 8Q<_C.n4A?7T2&*$ 9r-J;+TW_q`~$i*8gcwÇn݉ϱz@Os}cR~4f/zyջ)3zF)M?iT3 ?@J" Q?cYxKUIߖF䢚+"+/fC{ {[1ȗL_m+pIbldͥ)jx%Rmbmy^{59nKmK? d_8iP(KX 7!_2P5ڿg 8 XrN:ˈKY4 tH`ߊ>J4.*(m#Qς_KhĻ3[!5-'Uh~&{o]D:JxID^(zEv'͔7J`6*lBo(LG}PvޤNqys%jKTæNed׋G%ZR6Хѣ\vZ#wO-ͅ8o#!ujubǐFshPhΞ|M;ɉĚ ׋˟ܵDUgsȬZLHO7{i%zf2m5T*xejDk$͒Xx$QCNY~͚YϭT+`g0{tlNs;$X =#zgE N x=uMm ̓Y].߭dNs7`nI߯oPy9(4a8PG)TjregÏ}"DU$'!F,/ҟ:K%䨷=Ӟu߅ˤq'! OEW>-!ʥHZCTøqCj0qPJFtyp<>@aX8 ^brx2gsy8nԵFmCslj +/f7qx5>/ s~GrDH7 ޴\KsCy5NъS?h$arq|><v4Snp7ӈߊSm!x^DB6}ܲMf:GsĽ,^,D2H2Esirs:ZfTkdP?Ԙ+w\|q7 C.ҋR ڏ%3MK5xN> Zg7Y!]I v[uw5-/0G P~cfknɜMxoZ&T_Eb{m /c8jM舮'zP>QuK- _b0⃩HS9(Xvrt'0~?ŰIB8C /¹s!L]` v0[b$-QL ݚ8Bâ"$YX4B%#QnG_$]qG.U4^Oij=4T C` 430!P; ]_ }~)͡=x#1›->;<]NşO- v\XPAN4̲Άk[wE"$7Ss4T"qoA?˄: NٓAMISK>=p.#'Bm ~>Pqoʂ5`/2>`K6\ P* RJWgP-].Vˉl9kxXHa#|8`C׮wצ6% O#P/ؔ.D`d-Y S)yMO_? @SiryUCnl*~ E؛^)ن=Cw tz*TB 3 bʤh&zxp#/AE3m0 1 +u+-[QK.jW.aN(iLJ|& %M#<y#p)E/:#E}@U=X*JZ0j>ToA:6f`7pú$G)jͺ/sA!b<1dyːN ; LMPK1I!͙ <+h(tGf.0ěb?J 44dR-+t_p׀Zê=c޲)r+U& X]m/,ޔ錣 #-B{9{rɓ6Eb$#P2/h~ # GaQ)a2NDcj ˌ7rDy#N "h9S B?s{\*CVFA50׉0mt)!㼄HSq8Ϗ[og}B# V cb= XrD\3>:GwdN87pkƉ=bb@ES{b벵o"Rһ$yP_C%rZD]m&nFSD"C[׃WN@y|̤ݫm*ac7fxG񭐧##;jk2B?)?s~>|Mo~Ք`55מ/Bnn+$$ѳI!ͤ6V:G[~-,U_/*M /d$/*>B_LrNK\?)ctzff _Вi6}]7Zن"nfYQc-LNI(1iWgdZV4}F]V3"V`DzP](MjK+ BF5ۘ@ͤ?oo1熪&wUV_‰^e{Y;xj& :E5D-=G!n@9>cè- S2,%wF`SY⋻ߏD = xjkO>gp؟5 0HۨҨQ"\vF yjrZA&؈2=Q!p8#ธ̛]۝ Q]0L|7mr1<#p^,>T '# =w`cW~%RbrsB$Qu?"1UM_/`\? KJ@ͣ mп`-FL_KV߳63j’9ܧ ' aYPR8 UY2Ύ * (]ys5c1H Kht?BAo3>\'5> M.f^cfN1<lpoWhn/GŠ==-u Aeȹ≯>؛~$Ha?ƊxZ Џw"8 1ncup9:)WA(졢;{Cq#VV1=Q FE9*:INY?S j;hېѣL'(cd:$斥FGQ 7n*ymH"`|s ՞{Sdwv9*\hjcĶ4mMps Am;RK=&+3ʹ`#'Y~?x9>+l(`z RCX[*#yjKV- d9& Ʋr&Lax<J!kCXES/\[mN)Y$#YM:1aZ~?)(㪼}L%Pp7ao=ry"6-e}Ay3-ft  d24! *\? )+ g5'KS|(_QvQZ05B1s9[%ZAU9nmTbcj ˝{*p#H>D]VW?ZǝUҳʬ45YꪁSMuL9qCk& {?sp̍gLk%%B!qP5Iޝ콰|C%pĜ*F(%YfI89ULzSRtDBE`K,+.{(R%%M=l7)fK ՄN2Z'ٴRsUvGe6Y,g^GtT_K6O3JQ4~uk F0> ɉ{qjc ߊ@[l ]";̋gvfg5PB2X3HIiڸ:X6zw |†).mWo3i81O۪3(]·j}g\#Up{{<crW@BWZO!dA;:ɷfba<@g6#IP׶:@j3 `P_J`)Z]OQ<:.!DxӼ8ޔo]6Rn`K=:g23 !3/'> űP)Gr%&CCn۳!Sj ?DM>{s[TIuL^%r17rNp$^=v].P<䂂mw Z:)7bޖ6W[|.R5IS+ÏC6q3XxV}Kk@tDٍR(~wF{%73)38:oFB:Ϫ6Mi  tV>=d;ZY#;0zcR%Q|7KXF"&4S/:>@C( 1K6MN߅'N:V]]if[`(oL8* PhְIjSCA`FbU;@iw,\4orbš(?2_f=TBE&bM\ȸ}[ﺂY sҠ4{ivxZf] U,NS!`4:DM̓ iJRN ݈ 36JυUDXu6v@UD@llXSmww'5{8X\툌Q %tivlf>ѨYDN}= C`H9WxI?z& S_tj\nD{yr C+,d 8zme6z~6s&|Myu*D񨧚3"tF~|&tBۊYn}T ..^p2o?ތioI,7a/7]\$COc?)`;%şwnhs[ C^68MF(,QfG o41W*㷚wlq'9WEh@V5'+o#<9 |=PPJf7]32^j4|~roOP3R")g?02q>gtd˟A5Qur^'FYDձxXD%qDHT%i6P塼 R Dje!5G4?~$Uq?QHshKL vl͋ӨPпytg.iz:sre@_K&+-s?;O]iR'B]^on+ 뺖W *#fd{dprfPӢf[%3rITK{FXlIvM+gD[)o WW=icfu 0w1\c'O+hOf aLdfwԌ`;@ҵ1Rܭ=Pٻ~ii9SɔG &pj(\2nUHAM"kW_+c~bj04kϢD<gt0 n:I>Lz:A{)A l+&c#);Ys8PcAHPh<;=_7 s]#0/z4E*=^,Y[m:/<ȝgA$r-Ll߾|=Лaɗ Qnq?B1N?L.'oȰ@,!O@?҄"(Xa6} 슾sm4 L>!{ F9@¡eh:MgWf ek:-!J=EM玊]ƛНb,|vc6R5 "(BHq.M .LJA:8^ /DY_<(fPl= LK k憲m'_Fmqo=E^ _@5rEl|Xޕ/s*O1[yKi _cYpm?`ZcEsQ?TC&xHVd&uUlp&QhA;lp#U 9Ykլ%!vrBYŸs}SX Q~'(l3xHơ3ٿC`ۍ/D>5кUt]K1&Wv/ц#8)h{\/W,`9C:Pȿ90O8aB%rܺ**]6&6 \}FuH& ɽ?'.QbhgAC^Oљz{w猂T s gb,& 3`2\p6܃p5qusU`t?fJ)nݽ.H#pvx\# #M+{7cZ +aӛ2Vë+)MfBK 34[PT~VtoxuS޶3h4Otb^}w[}2"8svk1|C5ԧ_7UB?j:-4 is3g>@Q$޾ 65v1sxbRidD);#rJ/+HhҖIJρM۫9<[[FNe=9JgzM9?^]6P]8,l2Q, s"6-XMmt t8ܬd9X %A@]7)g %r:ؖ0\.K-،D]|9Ȳi%4LB30"aLcJ =\4, ?! R ƙoz|:j5iD6zn{ c׸ ֚Wipd]b~)8 )^h~`?UUv5PtՃ qΉ1/D2w3;pJ¬?16kS.S~5[`FZho!$5\ Q?2 U7pF$i/\]WݯfvL|?G8E)u0oE>gpXTs]|5?) z[T@lxcF- h`'6wZMaj6(,F*Loaׁ8dk㑡Y%^d[&^i[y_O\Wb %"&4s8 l rɩ_]„?8Ek Lg0%0~wWa'&!J;L7Q&\UVUׄ@ϩKQ[1K?LfS%{oRq)]Y~ܫ^b9R~='}L(\Lq/]<UI?f|:HAXg=Mw"U`gjȅ,t:UTN\F5} ۨ%G5ܙA|oC͆Hu{n3?В$R*x{DqZv|& t>- #gəHvSk#Oh}%!e16@&ͳe}HV⊨G}\f @7SMo`?/6 bq5ʏؒ(ʎʹB0[sɆ6kv.ޖSdRCyxvej.? a&ePߜI0P)*N2BFt'EJ[C7O寝y/DSCDyv4ܦz5%H Q!=Z|7U&.^rc]@>UjT)2 80]H9}͕XI2H}ݺY OjDٶF#vgiqti(FwHȸRKxƣ!%;Ppoʙd}%T-W,J>v.Dy.w|lPYe4? 2qW)͠A}k{mV¤o|ol.Q j}s&Up!jڒ.!f,U.m7'Bxʼ,/T), :5_b60j'hpOj[p.,}()J}}FTyZ)4ʢY&Q[lNp#۪xY8Zo˗;c☎Kc#.E~^ɭe΁nRT8vhZ}<~Ώ0199|@ njyJ.p#hZ'b#HFQxl:.vX2~+neQ?ٕS"?!X|dx۞|NAD tD鵪Mo>=1nGW3*4re`KzAfҎD1yC%5g @cRo\Զjť^2 RҼ,.?91"ԝdYڱ^X#MnB)D4y˲.mܤWiB5X#mDjk "PpF5g,պ<\p<+|L4_ݑ; U&Ə;Tvj,T-ScKYSn ;ƕ^vWRf*jzt8 pcvc !r2Y~皧\$ϊ-eT[rk(O GX1C*I`ʻ`꒼L^O$e:貱 ,eD%&cf!Tw6qT3BVb nPezsE]14G{ށ")N5CQ=Sk&AW$+\`?xT2t QA +Þi"مɒox!ev(Rw!P M،ְrF7dp83+0'fj*p ܃ ֊P3rfTr&늮QO[V:SHikZ\)٪"y;0Aʻr(ѷGQt0I[DyM|(,}(3g$bx+ȁ@5KnJO\EOE౶? ̥e+QF;W GɹSKɚ,ֱKdI;'V\WPYYW3zApY^ 5?gMb' 4zOuʷ2 uOo ޔ9Rt6mQ q<ɍ~ vUL`?Jl?'Xk{:m-+2Q>H/AO/F:aO7T=ZLHAl@='#ߍVl%TVrtjb%w+ɀg:-mwj9PIЭw/*_8#ժ95wDcUbҰBܙmDz%y=? 6&X?"ا`}t.`f-m^p;C#~ۈ mj;3,/fq~cg~]`5F_t֤KT|YyOn\}F{PŁ &>\KS2:˷ J5@9_<\uDYimb4VKfSyƍ`d6VeDN 81dQ #h-[*^O_iʐy-C!1~Sϊ Ø%t\q59?n> 4h ÀHT}ͮۨzAP->X,AmKϲ7J7ݬH2*IKTi+]. {(08z1'_PeTZ!U5wQd͌{$.)(hiQ06l *_BRz"'tpòY!Bn[@ jggƋų\Dul^)0Q>{0Sw Ƴ4(^́%\1LݹEuu-8N = b+:͋{n=t 儢+]Ak1Q8Rɯ/ٽ1%Ų ˓zKNzn{8PS͂0U ndf8Z);uf/3~N_}LڔWr-*PIk{*s $86/kk*?9X)|Q}93mrR,\տ[H uoqO~J!S8@Ia"P!(XlȰۣo7Pe6%td$Mo>D?u-frɼl(f[01Baj'16v "WzwtN`A3GVmlQZ pJ &!:/`dE`TWބZA8Gy %O 6>=L^8{v Ep^B.7TҚ0&irc P+$hp4. sLoIվ'ǡ Ic?K:|LzN$!¾!(A*^UpDL/blxV7H;x&8lj>} pYH0#]E gc-_z F&S;2 "7C@{6_ucTVڍA۫XfK:' h JFHQʯ>hNλQe<+ ֏ɡ#CYt^޹*.:rr3.IoiѪ~5N쒞b2meD=߈4Rn5}JN'ܹtjIDեЫ\? lbq~u,C8™]P>q-E4&anU -(%uuWj-t&@m-{c,ZZe#vS"xQm in38?lZ(רF;=E[ RşUc"5?_:֢w7{M8Z4oџ6==? C,3&ܘدbV;_ Ө?^T6cV.ۄ ZlrӁ/%{) !!mwA(JwdoNO,C XrW%~|@Ե6A&r]B'n1[yDӏWr.^X!֦9ƛԩn ɗTLﰣ` qH>-1Ŋq 5Lş  hNNbf֨n!($ܻK`­Ƶ5K̸0#l{=S1͕%n<~(־?pH|nq[^;oįw< ﶾ q."mR?%ŝXFͦb>M^lm!'#'%[1DAr6Q[d¤Ҷ-5j7WgA֡V3厴O'>S,*BSt)gEdހ@ȅ)O{m7uO䓱Ni .~'DK\~W 걺v*]ב*Ю׹dIfT<Ϯ\JCu&#WPcb9`Y; +@V9UHʒpO-9mvY+?H1SBD5l'5sg-&CcNmU_Ԗ*K3Tn2'M!O?˦#zx튚AY $vi tp* !U@h[z#00m*x6QK(q c\{Ig`V.٣Gb*K/7]eEBhUy೑h@+Eg p2u{eHY @ PzcC-&.|!61T; U,grgcosfK4.jBB IكJ-劗Ku+aHƹ.;:ܾhȓzW4Xp==oN4v3K"z3g>KW#P ϸ.xҀbnj]U5޷陪LY\avn=Hh:7F @GVa9#) 0cɼVZڮ.ޓ0iT1B^[SRv8_JEziR-AcesSb,W Sj8UL.b"jՊNVj1|S#]yp~NnWޏNrq3l6زSL RfK&It';i?de8vIeA#!J!~"N ;`$l ']yOƠ7bZF9Ä&$i8 F|Oɧb#ϰ>/Qr UOQcVP|҅"zC L&@!3S/VR$ev6ցE.D4о-dG%qJ}OQ"ݽ9'kJ͇IpKٲ'{-v$ok4[&XXߗ&AX1{d Q7k,Y)= qu5S'x?F5VX`\B$tD> ?2l/zoF$gmCb-˭~Jg.\Lh,I0clS,a=RA "cRFiܩO9"w3E $(Wb#ȫ4 N-$kahIռHZPif3!i&CIqXP6ucME@`;n"GsV>=PV(S돸St1N0 ^ˆuRJ0i,FB&.˖Gy]o'BJgRY[ԷQJ1w42ϓp[B A'(H4xZ>ߏkcOfh) :< gAguaix9谽0bLADbޠރ G[W6cO0'lvM{qШ* -d 9# u~!ІC1N'4L.{Xۈ~,G5YM=H'2;LǷ; 97luJ|΀d>Ag ޭRӵSwgkxaG*YJ#5mE߅Qy{P.gfy;N!D~$Nk8& CLS*g)hƩptb}4#)T%ogI{c;r &bz+(cQGIF t!L)hMUȵ6;2fIR 84M;hk ^kېU1'$-A/hmDlfU}n @<y}F.su½KT/U$ߵ!} x)Ǟ7y3EǺKA%ה\/΀ΖIGH|yIܗ+%1$:VJ|<~G?WE=GSw!YTmھ~e)cVK2N:BXi&hv4^s@2 B`^ͰqᚩиL&GjCfBF^tzbwX'H.> FFcQ0 Y#jNEzTG=*P-ň'> Dw1.Ԯ \b0'[c6uE7fFa57r\̽x+F \&,_gM>1_JM'S~A"M 6S՝wZϢ$ybLuāh 8*x+@PP¥aJWhOD)^a)LJ_DVhƹdܐ́Qc2st"**YXb>M\dp̸^Ry<;: JƴApW_\;ZA{pK[+[_SvW\sS&!83>w7znUm x]2>^|ecYH*9JiE쏨d_nc]80C Jf/|O+H&_͌!qKL4ϰM厅P pCcc՛j,5X!ݬt|< SQK)NsFؐc:טzʣ{Cy\Gޒ`sYm=ۃRnI)ru:ca.ل,2>Տȑ &nާn/+e7kٛw9qh$~q3:wH=G{bɶn!"F7C1 "aރbD4+Z{ L=uUƏKH^_Cג7I;Mf^Ĝc}՗40SsC1zy8y;Lj"[͂Clw~=G H}yj7P#WJ,F1If]'mX"Ǐ3` ƘOE3=I% %ddl0_lም|ՆrǨ-K `26tUe@,Z^&6w%{J\0@;h*=%ޛՑKAmݶ~#M+M)7Vyw9W3&uRG0[KAvb}>ϱD{~|+KV.Fv? ހŮWJaiOl!#0!d_a)'oaaթqIA+2(-.U,-\^Cѷ DJY-B Dl’?&yUfWB_#L;$ IV+qSg7/ek[h,2H{Q#Rٔn5Ia,DqTCoď'nh =E `"`mXA\{HieN`L *:N o :>Z%޲+v(?["}Aް~#2ѱcmz2 DP-uۜ!E8^S[I4JṈl(^Ub2Ek`3` }ZS\lp`,c SHKGrceccomp-4.0/docs/images/emu.webp000066400000000000000000001303241514205130000166220ustar00rootroot00000000000000RIFF̰WEBPVP8 L*g>m2G"!&{P enxyגIȯxCV`yr?h??i~//q?{k7i|Kٻp_?V#ٳyn]YM?Y3_uOQ_/_[/`-#~O_?kzxҿϞ gruw r=G~c^Mmo_o~o헺OEwW_?j(tirF7at,$ak VBN;`L>A,+H 9#<6 `r0Kzv+CyѽJVATDun5^=.ц%g'E.f.2vв[-"j,+V"$pa-On1c28!j^;᭼$Gz/~, "}ˋW19뫱,ؠZcY-;M|v>8~~S;M<>:0Gm$2)A}K)c9_6մc\T&cο C\/ BP.:Ŋ Y1!T!i!57Uգ! L7ES|f3ۆTt=);E@lxsǜqѻ:tPs_"k4y;m NuN)'}|J3k' +S%?,H{J0hV/)jW&{>163RE.W><(}, /+Y~aBy$LI䋔 /7 XhhYPB+ř0ZVJ/v(9S'R#PROrXj' t! ǸLGEoabj5>))~Ⱦd E{Gz'y=g(I{Ksآ{>6duOd<$;`ݩOd[Ru~F̥AqW.8/i3$0h!sآKTWp]˚=ݽod!wqZFNK~(V/%퉜atl)vAE;(O$J:ƥAֱr)!5+(=+[WNKV/b[ Ѩ͒uufȓ?*f3_ֽ̪Ps9]>p@\h}FG=Ubp3>S`VƗ̃FM\ NRGs>Vg`0? M}nDRj@0=nj; {/Y6D8|d^jnup4_75q؄NnoJ *8n-@DXxk+BLĊP'wknO#vyV-)v~chJƏ϶F.oal:s}- 9݋85HH@e.p}X q%j6C A2B W[<# 46(sX02Oj=}Tv3@\[ j|W ?3fZl2W宜!5@ CH;A:q쾄ځOLۗ0Cɦ"8G;<R8!vs:HOkkЭl2K;:.fiߓ{Wid+̭IO+eo:و !p^M&%-Aw:Z0Qi>%-SM{3x&Jt*f^<دTA*'c hbI)3dFwrì ͙ v}m1+"46Dtq}?vfs%#y'Sݷ@Ý59J1Әilzz :9C߄t pv|/`Ro6-XxK|9HwYœj7{7ȏ5=6YN!aSZ@4ndRg]rMS7h]_ҁ\!<1k񟥖_b銉?$gp2eZ:! z cd64sscڷΦ' o6Wx)eHбGJd{ѦeFXw_UN+ Iњфx[C"N<݇)ֿ&kcǏs?-C-A%PvIDyTP(3J߿CM7@h+َӐ2@>\TzcJ><Ҷw~ %+'[)5~)?s! WS=q$t"5m6^mɭVLfft?@KCz BTtW%>~bz@R#nOi =vE4% nJ/yF'Oz}lwYRQ9v/ǒA>Y~l*ը5/hš!CdDXՠm6ȍ37Me ZBQǬh$uGIͦ82Ɲn_e(6~$?V/93ݡHI^-#MDQ;j8CL=7bLÎVV@$.BIX|"%0x*-KHppZl=jw#u$BpÊ|19ѸMhUb]vLzNԀ设z{yln<%SV5Cpgk]n|ˌAű}\|k$uSvD.Jl TCBD 6\-tߞ->'?>fIO@EWxٓ[;_o<Ȋ71RNˎ$z4ΎC('ci1nϴ*{$1F@-GJ(٣OUa̭0@=NLc"7Q1G¬V{cXlZ~:ǡ"|[%ݮ2K p6-T.ٳ_&5"]+Ws/w(WUjͱIzЄu˄R74m: v1Nn9At @TM[8iOrt+Vi'up=N9 XZb4,$aRu r 3 `_Vz rA8Lȷ3Ad]$1/nֱS&&)VksUdžp)\f- :-JWqvQʯ-*)BvuȽsƁ PK9wuDa<|(CX6xO6s@ uH3(Ags%^`=G,K%!yǺI':XIK$R]JDS +7HO=wJVف^9S/8}$[xݞw,>̽I;ljh%3*u:M.lCTzşw0H#-;{ǀ< `޲ 9Ti<,o=:NԸ/<A'^BB\xZnq#QY [lޙ7lFrGm!XH\/MIK)R)ە} øn'd`)cLdB/uȏQxʣu"Pw[lZXSDq;R3Ctvv*;Y)s6L5f 3Et<][;qnO1 ҭsQH;2Hlx)jdwk]:ס厙|7ZK K28 *ִD.1z f$ 'hNfQaxF]rS+& (ֵhG2lG!XefIeNwD=ϾŌ/gԱ,_f=@(ES?|CX S`'NgQ*k\`7*E->.QiڕKԄt~tZtj@5}M$ΕxԧM>p1kq GDR!g9y$ff%t~E.DYOݔu\jY/mF| aLjE!G1RCw ;q1ʯp!VXៀ؝vs2geL;x'Da&/Kz!e긑]ic*O ։?mR޿cKBdUD*s3~0J>U+GJAٶ]I =U\e~:nZPp(zlb{ Jc!~{[ QyvD-X8xe,qw&Op oɾ25-τ/cDn5^euHquN?ȹ$ZOz6~?3>p]G .,&Lm'c!JrXq>ϋ0ZH{XuaJpwav,l>8AC XZƥIDŽ/ndQP1μ!i,_E5"aMa0s>@=}LTom?2 mL~ˣ)g?,WM|D;5a! ~ }Abc`} z j*I^ݖ^%F_>$W:_MA8TQM'U` wC3E . jaAC*lRR\1j𜖖F2)yxj21ؔ+pp^e@jP ~)'Q )QM`V:͎V7%j4ي(kO/3ʜ,vV1E}0(v`u}BN3! )BptA?,+d_Z;h8EkL!Dz\|<< t,},$\Jbr.eF.C>]($'%JZ, .oƝtoalBvq[BiP^:VKi5]\oR>v"&UYxCy?7ҏ|> &qÆE t7 |*lmW[/_g_MC1`݃'f)ӨUW+GBNFy޵!FT`tǔs"@TpUFKo9&9A5Z@uY)ز78ː[ߜ!%&1{(WTu(;EþzNpB)~fYmwsv_-jYem&x=x39$uu[+$yv<J$L =:\&*6*׊l{=Mn0^a82YɣNW'.x|]ⶍp)ƀqsH^/e}8QC&Tyv|zPOun`:xNh'ɵЅڐW w XG#`>U2{vR*+`~-;T8O1Р r4t81nI\̶}?0AQ: g1QT^(iн*@7zIRWٚ>@KllS}nC.pF!7T9Q)؎0|ςGՑIўIL {gӕåHE~6&Ntt^dtnfpzSfC|`l^Yڗ֪O*38KʆnF!]6|oI,C^ֱ?DFI6$&'о',G;`Er5=jHtvq(65w\ФZGȧD=y g}?2{.h//yOeIa&PyL1zy((IaS~õx)ri 4tW@Wwb)R= y\O)"2}G@r􌐲l?tjYH>h?x bpl)*BDҔ g^1.:nZXB=>b2*:șTyX x_hj-wQ gA8`d } =o4}:o efkF?k~`Qh2x0_2y9Ӕgʼ"%9pDd6k]V6&KQ֙xg@>% hP 1P%>XȧN;!*{NA(f" fw({._aE97)Ih$[}Gz67CJCƃ<fxwVR!rE#j*@eJGY]~402 XO㍆4F𭾊|z'bjc&bH] /ZQF5Ds:B oPAWlVUmHOc VN*k~i7㖱 ZAJ9G3\Fʒ]ʧҦ=D4"9ފHj? Dexatё tHV":aCm_i#Xtv( ',0ue5f<]fU;ٛ[juS$ܼ׎ugPS΄+{+Z) ;L\=B1Y7cc@Rnߜ& *DlsDb.){vE)M]N#rkKn- Wϔz(X+Q !Gz9Q˩)'zTNiQVJqN~c*p* ?v:CݞuՎOeL_I;BA` rb`W\'ZvnN_ 40ųmbPN1Gـsxjk{Xh=]j{=ިP>hvw]1>'#[/7ƾH.ymܚg+ ai+ǖ\q h'̋X6Et:Zq%@LmA3C q5tz!;:PPKrmEb&w%q3IڛdRf4vB^d)/ V] Nگ OXN K_V2j}$w^~OPRKm1J۬o;ͦjkKd.q-:qgVr۷4=0p FD$~^kG+O'I߬s5i+wu7YjTh?g"xֲ|cD%EL+b:GOij t9-0mB^w08fkwP8e4Vjl ;i-_sd#,8Oa.qx /ş D=Eӝ˪ivkE=4e4fg%9#|zۍ[C=YF71?M [P ѱ 5?wQhfg׸Z(VjRx>)orXf2)2,T~oO,'i:;XF-hT DrMB6S~̗{; lzl3V ;F )8m[٫E}ѭX`α#ve[:?+ QpHZ#spmǿ]1fof@,@o,!)Ved`}1ڢ`B&́ܐ&`3NFt3yT*ȉ˱^_tBAR CdyLJ:15Ի7&VJF#_\?':-ƃ[TSɌ~ G^5HZlb]FpuCP_?9 CԒdfz-KJEF捋+h0Z6K5CeL%wѼ˚"WpGwZvWPkNQ`'Dէff@\w6TՉ4ezyЋfwםŝ{Zl@ HV--3otT5s3{$o&Io Qr5@"*99ߔo'$C/2{e Aif^gq4cʼn3T)u LÎ"<5#DG!Yj7~n34mKqfIq-q*%epLpA| ;Vbw5FL`XeہN,&yn3(-6 3kL h;|M 1M8rrŪw^>:5?a+Ad˽V캷9a<,~ЎL0CL7)-5 5+xOͯ\];VRm)3W#a:0̇1BH'ʁ3j͑~V3$4˘vtsC\u;ظR 7oy͍GHPYj.S6b ٭8{#O{n`wӻքِ͡E[LlWגϯ\pq[D.P;2 ]|]kt0S{_t22~BolK.e^;EGW^+^${dLvא2ގQ| ~MIo-O?؎"IN.[SY36A-VQbiF2Ӛ⹻Q24BU[`4FP@8v_#*ʛokqmzwyZUmm$bxRqdSiqQOssp\t Iq1* 3_yȱr | whkoH[E bF ڣowuCygG8x 2FOxg&=Q;R+zf4QcI;S v mԟ3q?r"Lv!KxhYZ2J=Jj15~teyAB746 rRRW"y͖_Ĵ9nW"` =J19ژ Dь2]l6,:+BZX_^$:\I;r8fCۦNwіm)-Ta?v WɸsQL!,ҏ5b< mD\| ]^0tlKBSE唋m6L*~@ɉ}bb'Y_:bV9澨*ZvJaΣ%6=$.[f]9wT#d_5ٖC%N`c~:]$"Bmwo8yB|w4eȖc QJ^56BeT\WA#8cJJ#g߸;(VNZ*.֨w1CvtTqwENc?<~]b|1'^#0'+/>5>emɦwtIFԚlk%}!@|#P8$u̕Q4AcW~GQ܈ #2HIMWu \PYS M\됟Sqip۔flկE2]HBlNP~&o]ԯGv9Uq|bTX^=8̔R@GV1A `[b1q,Ft)o7Zu/;#i,pZC} ^cl!:?^@.oc ~Z=Cabh ?r'eF

ydU֩p9RrRƢѳU< z@S9zaCb PHdqXw*FBFZ[}[~(۰ćXhbttiu b\1I`#lV5B(\Bf ކё5P$gh(/=KD#љqj-1Q \fƴL,4[9NmsIelo-G;N]_"PxMځsfǛhrt(GEݭuFW9fKg`W-< pσR6*ofu?@ɉ ߣNFD ^/ س#:_L,ԇyv[(zƀiDv=ܑ4+xAVy;OKkF 3TKƨCm5OП؍Zqz/N x-Bs=.`>'ێ PITmƻY׭<{ޫgbr)_b0hAo+؊d h6g~]X9;b@jpp]H^-y4)9lW*}2>r @>3\GN ,8czuOD U|>n/ 5X;(ԉݗe X,U#frտ;&n[e_[* DAVy虩/=w ?ֶ2g^3|im}9̻!U"/$" `iƒJpХaY.C˃SFr־3&B'e?.2p6\%oт|+1 ~Jm`:~Z+9w'V+喙+sݗ%_-ŏ7sQ]&Y R = DFN=d? ʛꮱ-.4s0GAǍmi&TJ6c7-jQ<=*p{!(*=}IM [@!m%!qF_~%x:S:P_L[ia P<c0}+ VNKIݨ#, |& Cav y7 [GDO1fK/Qsߕ93'ϱ&8t s.mH&;7ם8t;;f6Y7_r ~gzv2\fJjLEӢH'Z䄜 bXꩄ{x0ԟGU+*d_%"B Dܭ9/ {mY5TE2U<8Y,ve^D|]_ٮ6PJ:wg<DkueK;t70tml[c ދkia MCweP WkѐգȐ d tMx!.#xؕXβQ)~@L!Ο&v-y7Wv}ɈOxD i7Gn)@kcxTNU\)ݖa__!˜ TZn qgys3;֬9XҭKи^lV(B]݌'aV%CKpd-}vFQaîq,=ztՌ}j聱USE$PA˟U2ŀM {W5I"FaJl]SC,.~2Ur`q`6A&ɩ5#"a!2܋S+F< 3P?6-={2%vpy'ԥY|2'&z 4`2"z7Mu_:Ѽo6F#5{KLgA}7Z[<9 >|,nGJaFHR p4-B Q'"B7[IJ X~O2_KbB$vh~a&~ZI^`g ($F@`Ш w>SkԸ 50~zH@J/ۀɚ[L! nH\zoҠ+~Jj2ӟ.9{DKOzGKH3l}OuHr~L s_RUp<.Nҁ0c`4.۽)ˬ6#u 3R/ d4.<U1}=Fw)K@ezTYw_HB?q5Iگ8g g4 2@}SJ~eP*_Dx,h5N7B f[28Q14p fM [4!'BmK@za.=ۺ]ρ倐cQvu [ x4#7Uā0Ttu]Tx,_.\߸=V=J$V"5FrbݠVuhjdN-FߠGwBG_䯔%q4˂ :-o^-K  Lv#s jCs=r%&v~Zͪh XadlU80j>BHBjfZؤŹӐD3,- ?y 6(˪ "uj_d2RpfY0^?Ero|t1Km{/ϭ`HYBC.Mպu|3*[]c]g (ŏ^d]v"V̩"FI*'q +C{+hlUj(9_tyZ)=r3 '7/mߎqgV|dKwX;*Hv)S~5HņlIoEC(q8~r5T"Ф/-g!uJ w\4Ώbݢ&J _`^q4ŵqOGqUDdAE+@RySPo04=Hl$3\[԰xL>qOҚ=l"ؗSwn CX䆚ڴWx&'FJhq'L]>'a{/磿v&fzKggh>H7,M;I_{ s\gJCr0G$+`R7=KZ[BCbH',#z"%eԾU~{!ٟlr?P'B*"grFMM(ɕogR קt_bLuD 3/`tNZ`Y4;)'^D<|9qbb1L'Nwa[ATʓL8 xTb,̷CHȳ֤𦧀=ׯ.LL<7$Ǐ 3 ◟9Ĕdc7Gܥυ% nW$}J4˔<c&ZIDXf$FԂz-f~- & 7>Q2d 9(HL\<`qHyӑiTQ_Qy%lN×sb !QY W\f$yI&V`GZe@IOJse>ÀwV?)!<qnWŨp1ں}e0֞] ,Nz 5]bP + u-d6 ˶@"x0О*B0ud.9D/BEݮՑC aJPeBá7^\๜|RtSjNѠoYu.~5 3k; '1z%ީLr;+R[]L㬼S.XOMpd`}n cJi?w8p 7A1DK^J\by׊#Ω&<<.NnEW+{O[$#4CK0P4Q}*BHg9a9^5,i*f#*u-< b5H6E47 =LR=8^yI JT4gU Ed0J7 s.Ld$\߼w!Аp$'-XMLs=K]ѡ'Z+@MmOR6ݶDH}7[tBR=&6E富L{Z]LBi 1m.O; )",_ q BoW ŕk/4IwfīI*ʱ/芃.+}1Ў+8LCA78\>6Snb ٝ(DFOrzEL)n7C5ij?AcĘHjP *  K9v}MSc&ӿg`eM%~htA.'d2HF9U}uS2P W.Q:PnaMi.c>IZv󵟣xuk/]YM'ht ,:V#V^Z9P W.OȚgUD :DP@#JIYSskS2HlW~`vʿL{nI =V9LWãM]?/]60ڪLOk:+SOg=3ѡ}3-|#gRl8|k ӂ~R1'94G2 ۾ Ӻ,>VlkY>jJ5a8}( :[rzݐy'Nxm-M (WSc ~ښd4ݭW@$2x%ͪ6arZPK=\Q6~s5TL]HB8V,E>A EPa3TVhpoqsJE%qG$Kmʨ?͜qqUO O @QdRizŖܤ#MnT>BiZVUȍyaNz۱Q},[5䗂Lta(3IgP_G׃ӇLO)a#@QLKTbaDQBcnL0+Mj@MeLr?W3?zwMN:+[QP`9jyv'v+h(o`e zfC> ULCVb2ylnY0?& %MWG ϗ$2w52z-X|4>n  Zw@RD!RN(dnI*Ř%Kwݨ<ʈ5z0~Tfl^ \`[; {׏<+;0eLiL( !oK~>$eDSTQQcwC}/Ӏ7vCWx_m;sVDj`smU45ŤJ:}637j!tՄQRW(&dc֖cMu/h7_ǻfL!+]V!!@S6]$÷C 8un\ĪߢB]e˗ѵ/@$E72)f_SQ\/}&nDLփ*&{l /h~4 cƹ #1Woy)" AwQTd-nSf8䜝ˋYJܙK{f7~)qS%AՄ_#]\`a8 d# WU5ɲNy뛠 |2ֈ(яܩ:e]V܍T(m.E:[k~ rjq2}w6 4U]~Ba;vgɯ @=њuGhHH' PG|%ԝQot8Rncw-L'5u0}4T:2b՞4CoR&u힤A,8#^ YM>\' Ob]d W.flSRٗ#<@7Hr[h78$J).a4O4%XTcmh'}xkN"(wˌ-VОcy׼f+{2-Z+=,ګz4&!3ϡڎUjs7u͑gQ遌~. 8N$CX=GJםJ \,` _]=+;Fe?ʇ{>RٜV"XfQ7Nl*>P̷l6b4/|{=y# TwޏeؠM93' Dk{!6xLm٩0=K-ahȖ!cm3VGr)if|&z4M5Mbz ҹ勢̛{0R< FЯwwBZvAPq} ^L534F(0@]¤rLDc#ӄT)+ "i?0xMJvmiՕ84\ )=x:Ȳm5I뙖c f5?Z, O%9I<%[o'}"t  K'fȀM nG60)muwHqxVɭ4}N$. 1Nrdޭ T:h׻ (*:OȉDMqfù)LבΡˁ?jQgQ[K\*hOαy:Z78NDB']bLi,(aSCeRW-vk%;;ESғS22HBu PʧӠ9] vG".z6D.(: MYzkx^Ӳ'7:H>RGW؜~1 + :)}%B;܈,R?Pv1G$8tմ3h^6؆4 /.1EQNޘ%݅|SYaZ nfD4KjTY;葳Cٍن&Nwnۂ}ZC2ۅ=8&3e585obּqd\Hee:9ؐ[/nEz%cdԺFpm^Ŭ@-{Z8j2R7DYǤlo]65(>ӿ>#6 i V;@NC:<37 mGh:Q/P ʁ߫~L}iɨdXØo&NƳe,W1kuϪɝ< 0u_I䷶^㈋pS\G{Pj[^>ZY*<&ib?cI zΒ3_u5 !qrWuT܊!Hhl9 T-4Ƌ֎B|d~KP oDȥ6:HB:7vbJgse"- Hu7A'!XOd#vCeIR0\ Q$+DHkxK7htDT2֝]a<{kao1koox[+T~ӾvE0)CW?/wjպ9S Mt~{%C4ۓ+nUwU\zm qfmZ9NҸDZՀ ]!G+ksxiJ:/JAl;wա7R\H.E÷ 69kuesHi/ c4sN);e魨]* B~#C`kk_W(/|C7yt>0<|\Y(pY47˙3I4a:ZTb>9""aݧӡwE@u߸Iyw9B iiL_8HznG]ͯ6kN* :Q6][t%q5oڃ;+DN.Nޮ);]b8=fI(_*'qs7\O~E?Acei74 3U,2%u*Ԧ|aSK1wAm{)luvNr)@G,'Kz)Uy]1Bcv*%R-;ݬ}>-_0? smB Lhmɻ%(w?v,xMQ$B-xTO{@WľNv q=<&@s(*ge>`Y'8I!kҼ]z'ttctQdDThzngg ]/v_r)kTB>CSw!  W9ԾbLSώ-pBՈ6XƎ<[̷&yl@%}ҌAţRoAw"Mc!8!#xbUˀedb r$= B m_9aO>(ޭtlY2ΦwfQ6_Qyn Ͼ0g 0(_mís%HIDfxJ ~ڶŻ&$'v;!#HfiK]!8Rп7J1ZC0Zn 3<\vܕ*dIW4 2\QAuK4ہ% $(>vj!~` $c=(h'ƳN-ԓ=LJ&5sc=e.[C5ߝӅK0sP%҉00  3@9ƭB d\v}Xg\wB% ,n Efc< fY}_yWD;4.MKaTQ)pTjKT hlxTya'bRɠpS{Ɛ%% ltpCĵID=>G52}OGzn^Q YQElVyUy(ISp;/`eOL0y&m!JCeG`!iP։`df\K-|ߴ) hL~og<u~sL+UP =)(Ra$I0ooSF,HvWX\u,'F2o@}$(rY?L}}?ΉTnʞ0#o>{%jkq[T /M;{'7ލc@NZ2 9tL閑kW/5&~wN 9i)!P,coqwYpI1Zft!,㈞l sͲ/`8):ry^M9ӓ5~W#eOL0kso*^as< VW,2Glf&Pz|=A\ߚΫK?õ0h9cB&XYPc"5ǫZ-x6T`VOO-zZa8]4%`B1PD ':oВN)5wL]gBi"8y|}MߌYЈ![[JJ?ds'1V?"h?lU1OamPrBβߋ{M]fG/LQvB7{b&:Me&Jh$56>,eڑ5Ej5)^:yZ sx=1fōo}N f8KD wkDDtemNu gfM0cv=h/.j;,ƶBQ:W WW5N(>X\%T.}cyY$|'S#+S *.e"ZዀɐxatqʊZd悮M#Ͻ47)V=Yj+OӬAMYEb1m*&vtב0"M=u=4*u3`D,ߧ3dn O3Ir{M9e~ #+z{ɾ/=/=n\0Wt-<*d65Qq#M$S CBOGY&9di~ 4* z|06ewςoT E/Ơ¼ftˎ'ǕG6Ia 04pWo+MԔib aYXʇ"9)I PDUbI&qݢtM|)KȰB2a7/03Wh ;d&9x~+@ZE]']k eE10Qr`Dw6ioPLyfU/Lwiq&t%U2O@i JL׳S9T~zHQ$m4aIF 6guDs`z-x*+w#ECuGȇ,\5¨YvK+1u깪7pݎKl~z9nQ<&H-gZ68Xͨfk^`ZҨw #0H>a0V}54w#S\eyX&~&v98mc_`z5eubY+5=[ c8vz<8*8,}"X31Yh.GྡྷF]K>-۷ #U? Ϊn@4kawc{$1uqʙ9 }‰ֿ5 l^öHK=槈fިXPS7JNHnޒt瀫:ih WH-pC Ak;`5b>%q/P |7Xo|Qᛎޅ2Wu]!4xVYJf4l-:-xh#=©Yf((bJ6% Qt@nݴz o30J f:Q|juʊ,w~ ۢAinwe՜PHų 0w|i6шXkZ66W}dXiK%L Ń|,a~GɞDE-q*Q_2[sG7O"0rj* 4Hڑ5$iӻvx"Q ` Bs_ĎT By Gh̭$_ّLA(tuj ɳp "}^UߴDqi sO0]HB 4# ׎r#?qjν%8} = FD.T^'8a'i*N)p'yE@ERe&2= Mȣ-j]nV%Nگ˖q?pj'a=U`D@Liߞ\v,ovCPuRs F$%-e(hz^BB@~|խ,k-BP0@W޾Ui U"ECW׽Rі$ yZ uV7,o{G8`0hƬ\֣Mv`5ª]@{  XAq }pҏ)mWqLo.ºvx0}q62eH*P D/Yfx O9X#uΦy~U̪"rN\3|`ȟvghXr G`M*M r F UQuL)|aٳ}[pr}0kŖƦ|:?/bzDIe[lU4o*xnz!,B*HЛc'I,9q豳$:IED@ʸ*6Lqᬰe?;[ b5YH:ÁKsi*b 騳:: 4p|-yGP ,ckԹ[bu(؋Vћ'X8lFsUëD+'f;vp x=1HAV&K1ƎI* >2dX6ETfD$rKeIbmLٳkT^ WIH\|w$5Iwbnqi %~{et\-4gkҘQs2fbЮOГkH 1]uXqA~rY Q?[%>.e{4O Uc?ڧ-:8_ ;`CMTߍZmkysKLVB=9 4)r)})N]ȕ2$ FUb" 8hѬ0t Pa[fE!;jHTM=&~QAjbN{ĉ΀19dP; Ӡ83ot$o|ؚPXQYDckUyX:ٝ5=!IƙLn|{_Nʨ+9fUY4(X !Wȇ| jDZɟdy 1 u_XI,|X"dokZ5/v5q˒DDiIU7{@P:]g[%˰!O立qY fYr2Qt tJN sqbqWyM_{H>5sJ D?{G0Y 0B:a[K󁀩d9Uqv1z`^Z1#)4_^cч\i~}#\ZxO{΋\bsG n0\yb6(~>pqB "XS; / >b#hDwȓ>[`pМF; ڊ K@VN3I$1&#aLGlwgsOG07sSڠ 0;5Bi}kH !E Ԝ` YP{[7$6Rpچ$!\S*;A|M+yÃIȡh 䆒njx{/¿S΁zJd=|<_(?wQa(klيC aZe+$$ }:2VgGBlz@|!R[/0<}(YS1,awmf?u^ \ݢ5Hg2WB # ~tbxoNJnv¬hؖ_CƎ ?x!wDm`R3n/݈YXd-uT` oʊ<#VoD-kj#Le24df-Xݽ[MC{.,ǂy֙fX_Z1& BG8<Ԡw b姏z8Ta0[ǥ3 }NP'™iWU ) ]X}CZBF;JFI3ćؠR>STSC&)ܧ lKT?PU*S0;H⎦m]V&c"RpV4#&@"[iYd P 6g96W Iɥ.㿱inox"α'8+,6K'zF57\7{Bt)PEK) I'P]{2 6C%$~ GQ֧@W~ShnD jz)U =1"?̚PpTM^p%ͽQQޗԊ$F]7AmG~zLV +/|cvH7 |zp\ūAbVU/47n}3LnUagscͣ {J݋î&?sD/ rsM$us3~Ei|MG$nT0u4列ldk.7u-*0kt#6k ܬ c<1 97X[v*xEY]R#[:sI$TxP͕07Cu(]QSɄǜ3v?HvV* X/GQߥ6gB%Yp@skr\l4L)mWr㱷x3wqFn`TbOSϧsD#oDz{᛺MQ&7 ZdpǭAnz~MH }U<5,9q>Xvhx}I08܂+Vݗ| |] Z:ž#cJwVb%y+3R1>[Zl($H3HO5A^D%r{֬FH=(k- Tx\f\.mFͿbpk3-`|ȋc&6Z%ت6vBFUЌb4("{\WH:]=G[R$'#Y_Ka۵{Huc#40s46}lM6N0VӁx4_v^Rqf[ڽCę]wC7It:¥OK>(-ʊV!`k4jB<Ѽ:0oof`+:lEM(/}kjخ%v5 W89H0XnfeXFΆ&dk ( 9 ɻr9m<- AWVgS P#P^3Bj-!t9% yqS׋Rik1ƀ3}?kD#s@MO${/11=G^ xIA>oZ1[V*L %gBxrjtpgƗGĊ=m֗ClڛXlUs<Ԥ_Qr|N aIOAgo!1[:?āK/RV.ĄqydLf[D29~Ǖ<D.ٖ+Wv`WB]C*p{U.#vLJvk2("`R+ I;DEjz[kt(?a6З*ZOJ*'jTipl*z.zHAbW?M2wY"S#@I4/#O0y2eK zךM;AҞo nC B!?uW q4STyB:P0 vִu;mSqI/k9N0mP+Dds^ [` hjunD@$&Įa7C]S^c[Tr7eJ \,pI j7DC0«@G#>uRٓ,Ii+Wh nF!}Z=lbcZz>Vb!bH.(Z״ñطcLM,T=RJ@L@fgp_?PKm-mZ;Ǝ\er{@.J8CfϞ$/5Fаx*^4uwb@Gyi`; B8TvT5 3=9dDld@gr5|ɕ0SMxA(fH; -hCwL+fPС^<<0*;72~PwM}U0Ry3{s \6ICJv>zE0?cڈmc/ -+=hT}\eKv k OzY ]E9ÄVSfAk v6Q2#D6X q3SV:â؟ 5dduւ)J+p9brDr FӴ }ZSw 1>~<M ?G<- (tՌΎWu~/ڥ:gX@əH^[}X-HlӘ2:N'k/K!3*浘#RL#( |­C{8e]tMx eou i9f:!fbЊW7Bs1m/*i EAg8'9;6zK=s _;,'Uݰz,N[oe+7` '^F=rW4R v9pާ ?U+,_< rSJI^.{w)AEC^z I;X ҀPRP-+Y¾T=gY ?[/ė%(m0G"!(R[ gn\oxg$>KRo?k'`0_DPoR߲?7_/O=?S???it 'o뿲%#w߮;Q7ԯ\~ k߆?) _?nigD9/63_oG?,'W?~}}gWq?[O?7-OLG+#r(lL]gܩ$ FđԻL+HQ1fNП D]h~X:Vԃ]C8I{ d3>sO&{Шj|3RDQl5Ek3fH@7(r <75s}`*[vCAg6B_qs _v\hAcyNѥs҂JC&r{# ^@z8tŹUsnW-w\.8C1!|ͳ| c=MF]IL$4ǜ!K7KHdH\H }pj Y EzTCY9f:PyL9 _6ɽϠm5;gYVL)|[H do )[c~T\p4F꜆ƛsɛ*1T>t9FOmYR1Ejħ@ I~so`- ImyhgL+][$Cʠe\Zʍzg.M,rU0 s䦣YBU^pAuP?t;8s %lb ou&Pj " ˢ&,Eg &D;VVc{7dK9..e6a5|H$]i3]/jྐྵ ܄/J0.c2T#/*A.iF,9 P/XpXGd +7 &[e!Z66[Vo'NؤQsV[6x2> )Cx (V4YxJ,a`4PŽr9^T s8Ne n@ap2!_zdKU$,{ Z<HAEkEҁE{$Ժ6;HDii+V;TU._\ oe+'qRP޻>(O}߾ꭞ..H? .E%pK_\ 8=k00q}8\؞3N.p ifɜȅ q>}[5ǕY"E@KW;-nD!AI d%33 }hCvzКa?T"aa :jgh-&G %40rT ~SMH}IjR$^.ΝG)l,3.+fbrEB~OE졘{fiﰼpv %䅟`ver.JD(/-1@d(|x ^x@cȬѷM;]1@!A34K3q.9G"f~H[!BQ5);tJrn"|C <`y&%˜ug;{}r^)M85F@L_R#"\7}YPQBQX ;ˠy)q)(7i7?ݳElf^JD=KBkv2:Tzn)lȉ"LXϼu&!,8"UH#GyF9I[\? z60Z3#RAX=H<~ ۵Q@]Y(0lO8} @XZojw*$c.+vx[O"An*Z̔fU, c&bUbctb:_cQ.r(ؚ%-+^a)Z2S@Zw5=C8s%`(t:l>F$;p:]&>*&hIvRZ:kO""/)€ϠrQgOjޱ3?o3c)IT6O: 5M؍O[XUB0S9̤F[ >vXWwY)lh8e놑b~b]4 1N&l_D  wAٓ^]S 2$O)$_7 Gj)mL)D] q*o#c- _ +HK%ґibbxrR'Rzcm7rCω$W[Ύu`GlRKTjpF .|αl¦Gg*ױkJP h H;txV&W>~B6==!s,yF6.  0$~偳1̘Bus t΃b4һȃi ǰ &\KFK'L7}BHJ6K`>wY ; bW)i9xV&.} X  ¨5sΏ2C]e!aI^+>< pdEI*Tja]5e:Uw,Ш Zҫc L3;ryEP 4SpN UT5*ʄHf͟i-|5zwzsHLnyx)s1n=6ǎf hqADDO$bg wv eKbs6t*9E|fS)PR{:}w/;!XB݈:@nsL L招WwO0V"xYbl8hӣVz0 /{銗"YD2`l/q_Ӝ0.ϬWp%q b# }R끂b@,ͥd*Nqw ZGW@{5.V$uu{sPIDwDwJozDH:K:_Y'5YP8}j:-rîsb`c(#v+aV>`A?|NkƚFկ-Vo/9>@Enw5z?Aֹ(9YcfepTM=9GsxV>=GAۿd(pKo*;! v: i}r)ѰX'9(=_[In:h5m4It eF2oI4k, ·?Pu -tfG9 o-QbgiNuh+Zac$ƽ_¡mme0pQSQ:zФIhI+ 7n{&| +Sn!Uyqtl2ڵ*9|tp߁ȄVU%Avl[vQD㏭Xb^p`[Gcs!m/9VzB5ٿ[ V-?,PGSTd01V,mNr{5҉5NۮSJ"hHG 6VRns^ulf@Sqiʛã4bSK !&R=ZW71'u,H7a^5;P!d{xM#; j)搦5iM4.->p iPeKoU n",(Ȑ`b]& \[ah; p(za W9O}栚_ ;sy~j\Fdr-Ų@ϛZa*+-\OF!s+mEɾ? 0%皕CS5rXo11ߖlL%Ǝ7=%Zch>"ӧZՃ_C j`oGܛ ׂ{AQ6Yk<VӨu ߄[9Pͽ=+V؜/B pJtmP%k,9GkŠA )UаȌb;^oZ@U 2C]S⻖n7L/aR1CgW?]p@ԯl`sA xF^(%@ ߨp0?׼0INB7j& l~Nku=~݂!6Fb$(RR#[@&ocX^Э¬dRb]ِEtKX!F*os~":2]F 1x9 2csd",ga呧{Z +S[BN/_ kg:Dh>8au2b9q+.\avw V+F=]vq{(A _2๿ 3bH{Ϝxt>D츲wgq <:nVy0v/"R?5VՆ}ܽ}yB'*.LcBf^x oU1,3v7l6/Bhcv8LBu'f٘Λҝ<)S͘k> L> |6Tp * =Y_E{ر4[h?@]EF/̩"Cp=u#U)آhD[[]|DuXM mJs+{uJ'е{QZkpQ@8ne%v48g< :z(p$S#ȇwJ D.fky}12Ih{:}3%3lQjlrjmnZOj'ԡOo3v심 s`1Y@+KQ:%8L/^jKpY͜]3y:SuGm+2zv8*ba#۷2;AW"kLH{آ4 $qgtn!#_kEȓw,?٥Ȕ[ X &(YE[$ol;0Nyt"*mytmr ohBiIw1CB1pݷpN*˸ $^ ^Qk7"!l$sa[-Cג7[+;&8o_G󆰑.Li c1fK($[CL."y~tcie "9RonFGV2Y9]U 8@aO6ȼى9pA2VG#z4]lrM.UzHox pL9DcӤ!ob\ oqCx˯rşZW[ 3si^5M Ǐ M3, @OFú܄Si-C 5F:Jޙt<;LF2;!eC(9(4O:jYJ6@4#E%G'%^+#wԲ&/!mnYb~L|K bo#DXm֑ypIߋmyȝw-A<&c:aK^H#46ƑA]KCt>'^;OL9ϫg o;"t`AJE3a TƠ?"7̥ *ץ'kZt p̮ap+@t/}6wVSЪm!*y U|7RB kjs3S狓Dn_).=> (*^d*L0OuMŹ=vA b4:f2)u6A==%D"B?E5exc 8]0p*J)縡wcGMΗ]<{@fέd5ck 'jkyohz7jK[o/jL8~ VqabtiSrn0D#v'lX1!/VbeKǼew,B ܶJ^:Z:A_>Se9z`tFu*d&E/g;/ -}6e( > wM}Z߳Gt\;ۑ h'+.GUGR=B?}\tLAzn?>$f|t js$[|YtVّxǴ~oM6o_h=6ð>T[D …oeEj~C֯B*:'̪! T1 líI֠%dBr#_R+倿/^~6ɛW_P;U%HcX\qF‘CpZ =4L=Xz{e3cQRX9V*oe/;|oܝz߼RvGXJyNEfg9Mx̻\ޗQR)h}@slҐ漰BYM;y)uyGk沾GhsQ=!b .'}?tU\g Lś­]m?3\wY,n>bZ]!ce b"^1C6_, Hezȝyo<7tf {Hһ6 *1J@V>ٻKI A:5V9R++*˜kx|">'cv5*mB#qY{s3]m @ߊt9⽂84+pHJ;'YVt_:qz6]βMO`wx褊x|AEe3I Z [&WΚuq.NUJL@籜jy4Y"6ҫl~~h' x$82/g[_{TZW[ qL),zh舦A2ac4ȇ?Z#K)7p=7;{-R0TI`n&H0^'5r\[oiSE0t`e   +YWɚ+##{r6aerF)7ϯxȠr6eJRW1s}bp3rHO;03_ '9;aT yh (ݳ fEtHM R G+ClfkȬ@MM2<Ek[W(7U,rTqrn)b8S8R A ɓʜ -5c ~އa)>n6k7H!uRb1F[aNMI'6~8/ZLa0a KFM@`ZE%`ilGR=@?[toֶf,hTm}[;Ղ+C]PGm{f}Z;+ J|'t8N,Ȍ \'( |1 R<ގb,G02XGضpc  rp`WdhFMC\ %Xۼox(hu;WOnZPKeM[ $8 e (>G`!è@IVo)OFmklnW؉3c94s"k9:qeYdXvp%vc4/wڥ3IV|ק% dbEq,{3%0b-M_2'͟rHU͐*Ս(^"ܩ(5:KyVCSxd1C!8epˢ"Iܧ@*c?M[2Z(.ı1/pH뜬`ύ4J\6޾$֯R<$3;Ip'P&<#^xwss# o y}Ic,eC߰nͨYrcV. .Yww3T4kvinzOL”(K)Q`CNZ[x|'mYJqL_&b_="\;F%fSaQ@@V1jwմ%i*`Yu +^jZv%:Gğ rk%jzj4|_ W@\yHvRDݫŗc*Y3yf,r V UF3ꁟ ]Q1QƑl3W= X0o]'olB. ln3v6B灚TjQ7*C(jFhxflDxLs*k*WNATَUnbtJsٴ W~yj9K$t2$8.>FPF9r=Br$xWT5/f " A;KaےԌ[c$mqaf5ו vov ,k=(3z.}3 >VESqx>EN$V8> Lzz5;;`5b-䮛tbVBԀr|㴇ҞID8H-LE)#'ڲ RߒiY~ֆT٨;tfxG >Ad<8(-}>mN>E7íg$V@X椴YվV\&)P+eɷRXsF_iE2׶1-I*ʺ5EFo(Z*n V9X m3֊ʴv95]kYl Rs6/إ@lwͧUfB>d 2g4.:6˳ 滋r# BMU{ddbw ^vΓ.oFH9M ƨ#+m&>:4+ɸxb9"7 鶌U/׌P6yJK>Wut4W> 7Gfֳ㵞Uݚ6iiO6)/ʚjcn]]Y0s[ \WE< \:|w)2~m8nچ8Sۼ9S=^RkBC&[(  ;#Ur wUk^#ڳ ֔e9<4H{ݢ3%D߷/n`9x.aQ&]p)DíDO(n fXmϢC;aiJ.#"U0yQ%^38>`Fo S`USySZx&*:,5$`n)߼*R%ɒ'tpS~7y(O:.g _`\[( Nܹ _HN8kE-^Ƃs|3Z\͟.EVbn.W3~ i~*#XR}oGB\< ڢA$fbpen9R?X8tĊG_*(R[΂3G_#Kݩ$B$4h*~4yɿ2Vy處amA$P}BVTcqVth~e2~^u-d^0k/l~@Q4XR)"1u.Z_oe1 |ީfc@G*Do^}Nm%ŬQ_ ~oݫ10=BV}F2si*|@Xʇ} BLv"\_%WDmˆz O4Ś&x4%kJʂ|jyW N[; _[*ޚf]-zqD,F֠^[X5.+ҨKM}zޣG%:SuquI1**1J4B fʮӖ-It5/h|e>p뛟 I %b`jФXH~- 'VQSyG\V;xP/dUr7HoNVO11LG`7yK~xzh[H{+-܌!:{c_6BN+R__tΏkaN[S" ֵk (>e-{ʚ] {h[) ^x7YޱgLW&2>ICAŲ$w~Ŀmf9eӲ_b: Dw"D\[D*m˗wƆ @8žwD6*#Q쀻,F7EpDe Ϣ (qߋ˧Y:72 =RB$~]_*MO<b#ၩ+ 9Э%)vG=5D펈 (}kXzwNewn7bQ-hA>z|PcW%;z W92ͲHFXُ"z>tF~Vc_i汌Ɖ-a hu.0F,i>rA _1K"Pd')rtrv}h"p1` v rBO>fY^#b)^#Է@;ۈKYr?˄&g> V7@ R!|.⤶W7dROhQY!r]Uݶ, wBaQ׾E=J-n[L}kSeힺw/!?`F M'q&1c !f9Ewk㬔/5L!Ӆ|>5˃X聃:(B1UEJ7 ?1t@n䕄?w*dxs ǑOmA,{ΰ\b>H@r{y-c v:XWo-瞡 N>^99؞dZF"ӝz42(]L݈>c>vU%EK炼ux4Y8.w Dާ#=kC]&&_MV̩"]cf#БWV.6%Yh&~ /wJs]w}Jn'A|Vs59gIm9~7T88[bK'a猿T10gg“uM*u/BҧCrY$H~ׁI|Q༫!nZ -99c1VP<ټow+.ԕ:}7Ku4I, O^UԷ"=^[u~K>_k3V?Ȩ`5܅^wD)^}4^/A-&_|9m !Nfy9j5)^Q\$EVy?B3_Z6Kf& ?9"9 J\@,nM5SOJg8_lPA66b{]4kM?@If_ijF&9(o譕5B˸ߥbX%Rr6B%kȺ/\xP9M@[&)R\4|.GtW5JF2 Gl!z M"w>H~.rVwvӬf~N G0 |T+=Y jG/i^&Oa찁CF:H4sUbqwxc+zxMSgqe!z}.&MWR81LC=6*4n3ˬkr `2jNO f7EY]ﺂ {/'T_;'πLvJaAn@H81яj742tl} Mʦ}HwiB@Jfq̘!J`1I~/p(v܇nwYTq/m@^N2k:+2"Y|θkiٗ5҂y(*Hbnr;Xb6Z_\+cW4o>?p7H'GE'1dVA ;4e6aQZ4SF؋# wD%N@%Ԃ\ 9dcXx#6sd^o!U$4t~yR1I؝dYAz(jnUq4Q"FT"@{\ {OL-ܣ 6lX+DvSkhFDYO93W e6c^J"!&Nf>r :f r!-pQQd܉F#t4sz=`wr6{AIjŌ\bv~Ż@g?lWyq3ZrXm/M]OGzR9zܬ[ؠjZXy D 3&#Lំ|eg r3*olR#AItțytVe3-]wc9B  V"+ZAT 8Jً93+lu8Wv>Ua8d0E&6#n0w*`LN݋tuٷ:ϫj$ w2끵7Dv琧Ic֖X4a"3ө'u//hq>H=h:3 H3 A^K#D-hտ$n,hV(23 k2G" " IҋH`J͊;qn񅽫bO!FvI. r(Aq@MJ}%uzbK8|ed[=5\2 >QY* 8_|sx xMދRGd~ļ(.Y>ϥ0d vըFhv,P}1]Wl)4!u5^1z4Le)ev8%`KU/UIB _wj| ٌ9I{t3mqO4MeUcIY.TAL$ўw( g4FK[ N|RKsUQn6lt'R*dLUR斺]#¢ }+걀!##e܇*AYYfޘ2x^.S+u,o 8Z/rn~?A C:~8^rr#'pc{/l FiOҫjy}:hgk>'Z# H˽}ǂ9$޷˨ ݗݖYJۿ,md.o#REinI3́r"#)ͺ ,FusbbEQ2(Ps[ _eR`=ú^TF(]@}i7٬jl<0 )usH'8~ɞ?@hPyw5, n9 #"ݬ̮I^ѮA`qIȸ>d Th SId%;L .G9.YAMOa>X)쩍[ژ9w-JmFʆf$,yZԳGb&7f*!>?c}eW )A o`JQh['#Ww*,ήgƸ) $^|}YϠSbAn!B*{S-A&/n@kC(<ڣC|oJ\0K!ۭB !m7fIS;&n^gǕE4yǭ;s|w.kb?(ONࣟ2,p>5s*\UQ:QsuI~mWʹ]ةGwQ9vyόQ)#yjng{"tN8FV3C/L)٤C ))fgs1Y[ͯYRJfiO{(iFjÐK$kGs6w,7a&e1oga7,}y,0ƈ/=lՒִh3*Ub<1XoܞmqD]=@H:3J{rh*Z$}J>?~瀻|}X2[ sbH% HylpMgl XXP0񾵢5H-c7бj Lox' xphyCɫ"]*(\$pv8: X@աM dԿB)lx60`_ejCI=UDd֖D#<&({t8/#XB[ -FS$2 ]@cx̠~7k}Ԯ 2|Zc䆊G-lH]6aI ǜwkx B~R~KjVY&LEEJA2:Iv3N4gk&Wa Nܞ:#)=m? Qh<.{0!2C[]k'Y w'^V2#~$ѭRx& h_I/ڲmA gj~< Тrҗ4xr$})]SݵҎnRc.D Ŕ/3د3{tO B\TCvݷT W0|bx;ާU):ԛO~qF5?vE;Qn:b 5%ؾ z30vfM?$D8}jm,‘րqϳ_@;{=1-pf͵N$'c5k[U4:lw tz8s8,xOG/|egwE>ߘz = Q ڷAtwSIk<] BiYٸ/3fZࡩ ~x|gl&Rֈbqڭo@!B[ϼ:];= 6 _HLm)+2$ɾrz uc(?b/:R@5$$\ĭe_tn~}X~̙3lO2Faqh+|tؼ%fHHr` =[Uؤ5uRd?ExK5Y= I9+s'!{(S >cQeY]1 0s䲓 vW 57n0gfä&䶉]f%+E~U>HV΍ߠT+6<S{oMH5Fޯ%uymDm/K0eX2˻ģO?OҷKoMgBGuRI2>}aOr~ 6.$&Jx|/₩Ʋ/13]o%Bj04Vh ϰ9LR:xJEv,k5l3M/8pQIhNhSPSwdBKE~-W pȔE(ilDrfZ{|WMSI٤]~g9hjE@2uso$&w'G JrEEmYގL(Dz!O3k;٣'|vlLߓ\L/2 Ǻ\tkE:F tM ]',и{8u"RgKiN*NL*`DkrV9C.qb)Bm].s/xDyq6ձԨhxuǦ]-#HiBU?n\xJ '{ /tMoIYsτʼ6&B_[XfGAV]l^\@ڻ$i::{"/Ӳ QHy6 s[.z2T jR|%Y zU5X&/HK;2^CRLD]QJbń|z/Rbw.Z$فr~ Z8^v@e{GkBYѧr602%,lN##RIJkvEQ}KXǎKhUVraCf=mrB=y y9wlE#wFs7 up)ϗ Sr>4?;rѲmÖvY7jZ˳Tm$kdUGTG ,i.}gx NvedǷ{X}8B*1 lC3nO,{r:I"Y@eX9.a& H6x#>'.=7b>Y.09}:=NHvspG(|L|9xdH"OvDTm%G&X/GE_ -66" mX}N Jy/I*]=?2a}BF_Pn2@):~$zodn|sЊ36SǪW$Y Aɬ/,e0ۃUol*%g?tQf+)un=U~ȑZrytcF ˠ1#PbI SW l)?;*M^D IϺ<;tMx8ifoݤ`^JUVg@c;%Ҷ@ܻrCW8Hi۹_;;FCv4fk^O{iٍ;d˽HRD -z3,]a6m-; *Qfj|,SB`AdX".2 p3l@%+J'I?aFVNUӎ/F&KkS&B=w* #58qQ|~=TIm'5L!V3ӛmTI dg%AR˙P&}x5u$23a+*Q(F[.i6A-ZثGǥ[Xe+L^f% cZ5KyiTN'M";菼JGMG(^oR$RxՖI/biy Kg[H7|M6WCf#&$+5RjLԎ4́nmyL9^]ĽyFDWq$q#H {ENQ6ml +9G1sm:do ZW3{uCN`ۡ 0(բ<oKRCɩts0fl`)"nӹ}s3\K7]z?eYKiOv*?jr,T%ÀBYM(7~ҕL X@Q.ų]Dx!r%Zx{K얷YF\EqR)SZj6 m $Xڏo㢚'JѦ0Уˈ/G1Фz1E tY+3\QT B`= mVu(WܠaYD2DWj>mK сλCTLk8Fx,A ~mЗv8]>?_[%ѱ_ Ebܦ~^PY.80 矆;bs$ۯyPT3k|(aΘAٱ\p\LƉ4'Γ~?h4❿㐩 |Ә 5UkFLY,]Z~`aq\19Hؼ y"& OΈV-K3q8X(n*1d#(9 ͍5!ۇdC^>'eJ?p)*(ܻp8+95FwqGa [e2pGZ)*SZ^'KjAa m6H$#"!%8 ent@,;ovh~>snĺCcoz?7ڟq~|;?٧Gp_{/_^?v֟ߐ^;?j=|e7 1?~UgCh[W#+?0?EQ~yZC?{W_ooK??whĶ-~gE1IzL˗µ"@jAx[*lN7ql{R$X\#|6jהyo kYGM0qWٍT0\뫔O|JDv;lWKeSBg""w5YA`43Jەh Qcno Y<߰JhDu>}°sX;~ۄCYlp6m?rhXs[`y 0Qwcz1VD+RsPYq?-`2<7Vs$SPw_l @H/G@ &!ؤS,M VoFbX$|R/OZqŸFS7CjBY)!)WT=wN^Zz{iv II$})Em ۇT8!۔c`$K+R/O|4M3C F\2LS6Gt<7?/L!"lmFMbU峘 d u7_*-캣qU>ciuyԛR b '%(=Ì=>GB،ltVWTvz4Ԁ͛'jӚHuQJ5 M%/Z PfpįxWa3Ӱ`ίcK)ziMK]V[9]ڪ-H:=3I.=p06(|@QuBr9gUs."de& 8K#bBh΀[׻D~=be=hTž؆D(e05(i+}yu E_Ii@O<-G0+KaP*hc%%vQw [LeF?a0l NV,@Hx,m7e= a})eM6ipqv8ڪOбmQ>EL9Q:mDB:;$5m;w U;`Yj.pXInJ5L;\DsU|"3+,*kBE:⥎5:ϟ Dw_GjMŮp>EQ=J*21Og^e 9鶑,b ^ԛ00[FF}R&)YhȈh.<;Uf}6//Wyߔ4 ,NpQ犅4(yH:~o:p&$"P̵O¼/$MSMho ^ z w }`m"5*L\b Us lߡvm=Veĭ{Ny6$rwfGڤ?C죆W*$wR ,k"y/V D:3ۢrXE9{:݉˻nNk)"P.9  ¯X1T87cfa]ҞkPV(ζ\Z#9f͊^9GϹխ} vu7f4*{:Ix M?lΙQrX`9wM(#T`m8a(׹$_ws%28T9},9DڍRW6p5dob%?ЄPR:Gt3-U,tlڒazqr@?7PsFL(+pnn='6 Gyj*~J!ZP!.?vrdz͵Zb9JQ',q||c"# dQeWTHm]h HpguGZҠ2p8b VN.y e-=3' mȔ lf>Q65?M .bb)axߑV\AݏQM4pJJCY*vedz$=qfF - F8敦L]- rk-o%N?\e e]K)Z,,228CWsNұ%iu5:>f4/Bja/U =Pi9!Q lɏrB}g.fӄ Qբ|U`,8MRBWYV8;"F {Ę_VL@>1QRE&m*Ow%Dž&ąY"#7aTY98D}:&vWQ*#FzD`oԐ4Pr64+5>B. #< ŭGRٚ޿Q3"/= `χ׮ 5?3ZkR3&AmSCu@=DS|0H/TDxk7,ܭ[MeN ©Ϳ*GUgcFau=g¨[^|úOU\cNgs\;:.hU;S`xݕ^>7q;[`2ë+= h)X~R3&nųH$n&|:zfBʹ7!̃#(NOgꄳDrn;ԇK+QT Fc h>ƾQ:#3,}ǣp:Sœm %/pk?acY5ٖg~Q(Jrwg:prz]c%>ErRexg$Qmϯ'vsS/bJRuiw*;^`]x#$ܺihi y@m| Lm H ~@VӍiÉ^ Fz|r5iQ\V@e቗z1}TK*d@+w8D+d(ᄽ^C(ŵl}\wJƬ1p<hWD:i)il+ ȵ'IK-PFכnyK8K2T3 A0Y|4ATn[&@pң~ΔdAD qAM>h *xbћmR~Oq%{3;|?ω{td@3eəa</~ts |xx6oM?m+f&I.MS b/(UeϪe'^GIӱXUf>o]I6Ʋ$yTlaߒ\+a_{S~x܂6Di%gzΉ5Sstd1QMOU*V0,ړ3i ;u4F}B%Qz,M0|>hI*3w[ ,{KaPȂCs>_V.ydJᥰqg0~~&DDeidF9}`m )=RtEM|,qetf ߽}Of4ր>19iǗziɡz=U%*- "]y_S{I3rWf7f^8= A'/gAed$ fǃ$f}ׁxSչRSm2RMVrѼX䕓^wmu})Ir8Ă>厳ޟmny{|zkNq;|Ns'tA uyxXbMSOg]:loL);:9v>;[2<&t6u p;Lͦl8gw2 j2Sƾ˪t6+)$̡oGD9*ۓfލc=!"7"L p& V(z(7 {Q4̔Пyr']W =$fgU-BiZqhb^v&J;@1oLq*•+&⸈MD#YA W䚚d>e[KSO'6"R(O 8 VUMvKv}:Ud,!-g PIj)o 뇌5*~`g!q#4Ť#yNѱS]8ݐCn6ܾΟ6Y=;X6`qWt!7q5Ku;fmh}Wqm=b/1EUcu,AE0֞PxTA;9Sg98D['vNc?J+ruN#}4|`QG /uBh6ܔ ifiqkg 'w59;A1Cud\ ͉H)&UHzBM9X~0hm~AAlc;v7K|N΍J$7kML9Sa 'Ȑ!= W >{+Id~%=KwePX3|q'8௫SV)&?/HfѳIEc׊M'/er=uv ￉ 6+7A($ :Shl(+'&JX7G=ÛDVJAڍC4G@ վʼ%=;eZݟ ~aC*/ؠ%Ú @ilS5a"7DV{zQ7ŕB-u(R(6ḡ GJFr 4OpO9J5]C6zʑCQwfp k9EL(Wbn`dr~,]pڐ"qƪzVHc;ozn4 5gW2lPW{Qy o+L=[ݖѾtD!/=N19`P L6 p_A ԫ'&?NB{.Zj+y (͚4pe!(%Y[G<^OSc$remq|@Ezwjw\|w׎F K\9 wf8o{ Z=TAM _>YcJ\N4܁8I>PwK_GFwkZ#+AUUO 2@ǀV9TI'v@n~5Maj*΍leo 5'^vO+#{7!3¡I5ÚT#A*-%/mwE4(6ב4p)GG~zMXƌ&.,=snv?^ ,ێDkEKq/9UBA](LGq Rx޴e-EnC ͵WrVoa-IˉN4QjKhri1CxF O[6xX"g-Ewv-KOq, `NFR_(Ewc- brTg^pr}Ʊ ٮx~?쩉\fFsH1|aKkPx4apevG&R>̘?u erˆWZËdE%%$5_w||-c_{l)f1oCSY1]i!h< qR븪kP'ALQB~%ːr? (jjY &&34?B&eM|KYOgCDڅ*#|ew#eJx5Bpgʎ;˅vm[hݾva * 37](~ߕoogΙL4wx]4:xO*|90!bL?M# 9n@㚩x`'e*gꑸK4U3)\7&oKտ~G}uH\*;!/։qf|Y템ϒ(Eв _T_$; *XϢ[ᵯQG@X;.Uj z^S-fиbKHҟ+ ~fʑ?eH@r6j+9mG,Ey9Ûlr>Ecx<{3|bxOI`Z~,Y0հ$$Fp`!e%ۭ}Qݽ* 8#PKac&>XٝZazcN:͒nBS I#b<6 g7u`Ň q(֔`V]SQ΀f 3,/ =.P=L&;cr'XL {DP^"xbF@Fx~p6dL +8 Cխ}؈Gbv ;[bX=7d.z<䮫|OGA]Ta7wO<410AE~,(m'F;1$" xG"r ,>:2LCR@ NNo}*mNjr;!&>e'Z?Sg c [#A ?YWSQOKr|Pzpjy `7D(@| I+]vxtSokx}h.VkCHw7V/Gd v =[[9Zhvl8 !nyĤ^έiȓs.TJܩ7K:N DLƔG~t8-z.kXp KRѡr0pl$552AQ>p\B/O{p ,?:Vs4O&dc̷"a2~@?oD]neL$}ͫA6"a 4S qC5`hjX6 SJ{7D[}p;PiWvnkA?*f|I3pE{amv^Q/MGGe ݀wMx8;]*A)Y@$stIg}i޹:g6/;u;bF ^I|g'BF4":P[HIzuUm nz97lk9r5@:ǑU=lVQ˄wbݘa|:Kz'{\희t࿧R2l,G{CãsQ지f]e rO"ʘL**(썄uls9*]_WO ֪Uk_᱓XdX^T ;)oX1bĠ ,B#R ;ćnۚ"s򒎇hWpПlߒ2wkC4kUvBHM9΁u%5B E-c{saUlkCtLsSdKW 4Ѫr=RvW T=[>G{qx %I\܋"BnU.Rr m$iu4 2OʳTHrnWOˊ?˴]+l6;2@<oj0qlk~44~ 43Ĵø!lX,nO:) 4X|e*.͓XU/W)17WYfBW!;McYkpO$C Yk͓G{WطJ1Y.TAOncEk "-/3r7 6+3_rjKN676}Ќ4~.q5 h }O oTn?ݣpY'>Fe09?gGZA_`5@$тnTS{REߊp|$yl0B"Q=$oGwl^Uyck[MXًizwFH^F:@?037Vn\Q- 7Nk9pRC.!N>gHP"*) }1< "?(ce|S䛳+1g"=eHqƔͨ= 10lUYqcM 5&W&4޴=M# /F0.EYA"n%ӛFYbWrߤELzu9IoqW8t-NnzɫaV#*^Ȯ* Z9V w?O~\ "i]Mi"]2n䉴_iD&V2 H, 3r2*V0GY,VՖb.վ".).v,g>T,疈20 ff@6h'RL+:dQ ocV"x75BQ߱C%P(#EdeB;VfUw!9 `aڤ hP鼚' Θ#[$c|;lgO s2 vĈmw@BK;3?X!0lCba=rXD$w)JW*5|~߿vݠ;{\Vwm,|aGY*ՔW{,M@YJ-8(gĔ%u>ּ\B{EwT? ~whwO\0p; xDg3!tphAe :w6VÏQϻ;Yun`UKQ"H0$n(1P2Fe5ÐDwL"yMah\,ZޙI%D%odh-i7vMؘ5n>з6&9eYӚ=Tq&{ߩ690ѯ+fc Y_-Ȁ6 ϕZ<A8U])i9-ձHA:x}H_+DA LGceccomp-4.0/docs/images/trace.webp000066400000000000000000000634661514205130000171460ustar00rootroot00000000000000RIFF.gWEBPVP8 "gX*>m0G$"!(k encQc8joY/~ѿoP?zx7}gO_~| ~ZI?OJ1wf=K߳t3_VbW~?~}Gyov{g_OL+??~wڿ_?z'/_iI7O??WE/_?ep/nj:jb5Ɖ󉚻#4~E+ْUFSbĢ#yъў1Geh,/`~6)4cojʊ&% ’U*o.664{"Ӥ$9K2vS4z4%>n7لR;Kδ]bl@4i-V-2\]~=R3O'-cV2[<c5K_3_Zr  ‚DVڎva#p\RubSC"., meǂ=|R=f@d۵_rIw+LHV]~Rq@e]S4z4Zg. Vεw&ğ%ֆ6H3Xb4\,Xq2ehZ<~ܦܙ~c~ :oQeWH1K%8iCvIsEyi6A)E#nI}e3q[5SUaiƾ9yN)p,oY-,#;(8 6zRκͨYHEpHҐR K`s,\&ԯT'g2¯X,,))eχ PLo97)"`v" #/J\~l%aA"XP(㣩mN ?և(c'he(N T+kHd '=BJg|täi =gի4|W tbDbpw@ǪCCKlr c|J6}@'DM_j+p$ q΄^ƕz4X5y)~t 4__{yQXLߴ’2V ɺP?L\9b{Q;ԟq\M L(XC*ep,1d3u2-DΞqťU'I%0(%յ'^"h/~?JGM-( $JUcz}vJ͹ɺNN㗖򃴞fҠloPC'aXn7њr]ً _6Ҩh-Flw&"gOG(xh7ES(.>[$AXwP),X_jPLڤٍQ4Ү;}5NiЕe/W4!,( N_.Ta*QMoʩKTjKwCm 躴p.2Iapߟi@ ˡ7xC;M 1y-GӁKmxD>#XóӖය8E]m q&]+;3GH.x~2wFQ1 avOEZRkXF66i7ysRmI.JH.@L>5.~y,,N`Ƒa7> y{iQ S4u'Ք=?X'#q']Ѥhi(mM:ޔu~k~.Ϡ|&h"90/>y:apd$rVkZF/7(]pu^_2\v5lvU`[ KMMI߿CDf]ɣj83QLIu)oM'Ύ>w/|@|Sӆb{&kb:q4t6K#=rHcy^GQ\M,p\Ki3mSjHP?{,P[^SϡiZay UH7_)a7RUC~kO1Iz-٬NZgA q=oCc /ĖlGZ>BM#FG3)Bs 2f~>@p`)βJ3$RKS ZX4R8{JCn%`w4אz6tQHHFiyUX!! |Eֶdϡ\<) Jk{B%!mp-$+Y`F"/Ip\&"}w$n4z̵Oޏڝw PO 0,ނ+G >Km ]_3pk6aWJ9;hH'w׿f{HGqXe0RڿZvQt啨xQPm re"Lh̽Frqy7lzq3q[6 -A&\rb*%e."d#Ă P:oMoI4JX`dT,cqsR!3+Sq|E`;b,o;qiBq~ C`Lc7RaxRmHϺp'(cA2+*Ġ[M}Noʮ4HWX,wFX/4d(Vf.W0Ĺ&ODͅ\ r$A ]Ok<-bXQBE~^{%\IBbeL~ V|+ HrXt#gQ:zºb1$0V(.bzAh?B5I4E> ߧ"98a-ȤKq`b2_:ŗ٘(H:durr2/Iۛyssxb'ҴFd J O{02_ vq^5j֙(8>]n- >*8yy2p_q X%PÐD]`W.qV=`~(6Mbwi( ,, g;ԫw]g%Izԟ8bٷ݋mչcgSa+ ע / ^#4tIp>>o޲Jrh[({0H-Cx(& nOE;j^#x&߷g 7VdCXz$1ty|k\E6!/#`vxEI`X#уayASl<vwϼ ncؗ|ya x r+T&@CZΦ|vW>FsB+'sSLYfz% m6Q+'|ƅ^wqcTriShkʇyļBk=j/Uf?q9y]=+ lOꬣbF%$ <4UD$_t?eƲޤv?-Գy x Gh͂[p70wh2jt/6dvzVufVk{TM-镕>PGiDlH?bkvbxtƳϧ=ΊJìy1t(WNJC#Uݦ}Ck  FzbVfK9,mՓ*`AJֺTmO![TVXOԍ$9tk]:VarF_a3%%M4{ebl2QFZ;3K65"kp-w[:2{ّyQHuAFJBGX7ĬշJa~H'>F> $ء ХuT+(!50Fu%bƳ3q^pb>EfkUvv~H Z&d OLdwwlf1L8|V+9W*߼\e1=ᏝN,K%6![TY"ALA}_ՂzўVgڹu1, ,h CjU_i+̃G`7renP,Ig mQP "ƘPTgM!4.$vqDA{I~,f%*J=+XvY/h0nJH_mvsMvC:3{7O?T#~1RۛeY~rePLచgxK#svm)o>Gh #%WmwRmz˛fӠ[[ h+b66^&m~翇(rBslE)_ψ7޾iPኁ+;Т/YkDHIO|9Y4,rJ Z_8Umsg9vT_uNئsd>(M=v[dK9,:p= bqcg2s\}$ϩbV`ϯ`Wf_ѡ2Dڗ~ÓMS'9M% 荊 w6r EKeүH'1 a{RN9ږPD\htXvdQ9-+A"bXWe70?Z1M9lT3JH8>,g42ī|M$ai6c)˅"dZh'ƅd>soi9Ȩ6bt|vAr Cz6;)Ў9Wv:HSΥ(:2R)oѾ| 3Yh%;v*Cc^tsxDa Yg}yZ_k])AO MZIGwT[6@L3 'f:lh<r-ٚ0S%bMϴt . $rC:dQl-Oxm~֗#n7V8t}&4c}FNzH ^K+jC_N2`Vk0,ED{~ h6`$ȝj0'h4wぜU%)eQF_ě: вb ߎfRٗHfȘha5#gFK9k̅ D6N_ ū9 wQ4 (]dx,3>icgOҥ<9": QZ-1&#>W3)v(Q X=Aؿw|tvRTWL"7r :ZȟB^=+ 3uJm4!m, }x^ϲ{wt+]i .9(wĈ.W@HeXA) f8_IM%~#DbQ]1Dj-օL+/=#Sk[4V?\4/ =0P\= gx/O Mp &U=<+6ʫBTnAjR$ʼ u~Ϫ⃍9DjFzaͫ)%!B@31LJg:f Kbi2(#$Z𐙐-s4T͏;|hʇDfyuC%z'$wkد&Zs'w7@?jQ0lePiwrEyj_g5Y6_}=Z=q6Gϩ$H xLa"{4H_k4R|{7b%:i[M@JR< J,J}iW9^\DdUg=[9+^[A{MGy}![rs7 H`> ̽0 d&cv$vu2+Jvp+^ RIQ!7T$jeZa˻fG9>e`G3WXT v9& UH] *5 `+9eKLZmV>P']1>C+٪$ `e>=f*_dL؉b'U,\@xtPqoQ{▄~Bށ;fsˁ[!/e*9}1TORwp!֙] @lмX HkQFz! "dq)_!$ gTxؕ6ƎR§@~'ޙ`$Я"Jg~>sR1 BGQR|zX0q=jd V`(Ɂ.ɚ()!\4m}7sJ$a~U!e Y?-U1Ƃi҅2cp L J=|m QcbyJ?@/]nV"_[H 2d.6D04W!yJ'AP*w-i[OMiO Np`gFn<"2@Nr^HT vy!W6y߼Ă6%-9s}އ?CQG3\Ҩ8{p@b0ؓ(JJʐjd肿Na@x$JX|֌㶔󒤼v`^Z)M`̂Wau]oHHѕrMzR3vZ M q5YQA&6遝ل"]c2S{f\l*4}[+EѹCHlh..S}0% r?B2_^lc(kk$-+?ȃe^( 'y՜}N.9~&Bqխ?z۬? L'/i՞[ؓU:q&qM>X,JZ+d,Up<9%} vQ *wKJ[#齸4ge`L,AU nϿٰ1)DJ5=$c&ҸNl4cX53.7WsO):3Vq'GtIa绚lRtGŤ#O&]{3,im R5Dz'xczB⟮t4:h/y]`F} IYۏ,+Y@[9tῢd sNGxyLU1e#*J*vcܐL#9;J"+XJɭ[Cpf')m8+Z7ir 9-%R@vlmH m2oae.n~Rwm  _-@J*QT+<3 s T" j\tp{1*L3 ܸ3ڸya/V7B4Fk<4u䵂/U~{{03|0S+6w`# ??% %˚M{'*1Sو{S;"=2]ZtV>P~D& 8 3Ck|%P?S^I7RU0:tR|jePAu?}4G-rРM_^tevX}3GISz7tKa_=0e-"hFDGQMvp4NX%,%A ƹ~41j;|fmH>U!ư~aJ$:SpFpNN4Tۋ1-yʑ,.YpbVifNU%JUWuj.Ceb:4N3ka <ҜG1e" Z Z)DfPB a}Dee$ tQ+pKJ]IJyq:{&f<èYe4~jGQ\/6nq]cGG9w73E,wf@_•r7"Zs+:bS {sDvPue'>Pӵ`ccFDܘ bI|Bf-@>=xk͕blWvӒ޺c&VW}uM5 i=O*|E8+ߺ< d[6Fet'퐛3 G8L-Y ޺22(*㒣Gp-0 -ըaPƚsd[*BYE4QF;"}WbԨ!FŒJJJ^b_xS8^ `S>zDy>^5U/Լ7JuΓ(ȺY,7W9^w:5J-Sh,uoE.xAۍ\Oz.s4tln2p< *ţcRԝY(68'ڂi NnT1}yjv 5ufAQneI?S%9T9g;5<+B*P #&=h#)u:tZioڦ*]P4u]'Tֺ\tQouhu327!  x ɰ/`ʎJbtKP^eP@x 쓣MŇk [,n f'2c{ִ8r<a`P5n0pZU}%({F;6w:g0IeۤnO/p7HBz!A"9X-J=>a{s3س$䑗 =C\|k# Qj(p#Е+_LlϚ -Qdƭ-#r|V7ʒpB0G9ra蝼ũ =O'*~p`j;f|Ss\Q4 3)n6ܵ0U楷>,0fqG 0H}zy7z prRgG|v`Iy3lv/7qL;PGҴp: SM`Meڷ/qfFL돨Lm6RQbWqB~!>4kU_evţDÓxcLP]>Qqb1MwUI>ͦg w24s]Y9ś:m@V9!-)`8&)99^J %vH'qJȹ!L0) z`Hڄ(S]d$w|gJa;JMy$n J*)X.= BFaڂ!z[&Q8p Ոy4ۡovRGpzZqy;JCczm͌k tG& s k?tWdҿ2@!oP*h6 d/~y : :|?7oNSyFǔJBaZ' Ce*@j2q7 W̓yb~Ft6qՉLfjGCEg GVa Pap8CE U) zh$pE$D qk|r""MҘҖ.D ""%O\BZ6w1kZA bH'ղ RsS`9 \} 1+XgK*<EߏNRT^~'dg TytI処TUdM?UA"N1-r'I޼[[܇c[[ %nD[.ns$ Y<][(]OWh9N"+RsW0032Pu7cl*D3U9Jľbm+3זiCmu6v@oZc;\Gh>ĹM:d!Nr\]]vLPI.Ӑc),Iol끫g̘B=ouAbj05@Nr>NWܖRZ4𕺗_͐yF9Dy3Y.BJQԞvfh#}l҂(*9X$I†u`/?Z/m]E"tofn>\i9-K:WF [ {[VSq|VRLiq_zN ۞9\%98ਟGζJ;넜,c Ց06\낯§]< y 3/CNشY3ހa$1E<(ɱghս;ny3{> , cO%0bRĉGop>rԐBYJ:ErR S*Tkkvkh!KO=>w:'&'A4굸ٌMX9+b:o$xԭޑX?|ґ5I(r=xtx&TN*v hQ'd=j?4YX a9FT kdOa4A3 QN=e2hzH5ت=}lm_%=N͖gSvultU3~7)-<"|o) !6,P%tVQ a2H#P~sCj] ntx0ru dEQD덮FvJL&yd(B,d|l=3qJsx7^4ێVhu ufpzTe>9l*I JEypnA|둴LH(Xmb:3IR@jUPRG2]Ņ_%ϼ(P8{x =Fh eqs){c۪nynkrP(ԱYUlW*uvߧU$^ ~U耄4,ғ}A+R (Ikʷ> hnA> &ϰRfrmg/!MYwjO.gjlMA*E߷{R1I(`F Yq˾ѩ e;*$Cem Lyo?'m-CnoZ+jzu'ͧ.H`9Ry|i ָp˧IXS68QÎ]`\n?).F Mc ڋ/0۽[Vkm2wI`6m&il7+~FϘcm]&jb[p>ur T׃I9&"JU'ߨD B .mǰ' )D\f7\8`˃]=G7Cٻ"f3HJrQ`Xڨ .I&W59<~\G ~QuYjѢ'?S-6 ~%iTۧ0TkRscLW2w3`}S 4@oJ)qnnAry5<]u{[cm@,gKߢCD(U%7d2K gwQSSDz!|mzs~Z)(a΄XT֑$.Hсrg.8µFUxlxBw e㡙<ӌo>)¥@:4)Ϝ"j1=-cF>C*^\펆!/lzaTNLmU]Eze|Z{::Lx牂H@qq()`*iqg'FIǞ46(g1s|pZ] )hV)N֨/݁;ӇYkq'Ĕ栂_2w)x'ǍdLK]N_'$Pu'ŚUlDjI*p?a(aՎkL:{& ͌<3D^hu~~G4S'q<=dhY5tbCg!& VL򠫿V )0K }w'Te`qxWZ_3H$O)`mˑ 5Y+dXE0U!ف㥍L=Uf߳xI*1eH4G! ]:|}ͨC~c8;-,&lSi^  CQίVs*,ft˓r*~I?H!eop.l3ί6)feKJJXjo ,=Ƹ2nI#@̱- 9!F%-G7F ZXY20!Q0S׋>ȯ{佱hC/9UmK\_?-t[!I+V`zW9gΔkNqBɞzJǪ̡9mVH1~6 l{O7 >pjq#رp ` P F6Cq]k g佗`H5.7UG~̌$4c .oȐAf>v6n(/k.*jOq=[q獛9Lc#hYǬDXOi[(|=vKY2Ѳ6Ŕ9[և+M"C*Ҳ=  GRgr) gwчz`~)NqP48wj7eq zQz'b~݁Ʈ1ULCϽ=Ӣ_5{YzcQkCX\2z۱#GnQ #L .b3fi1ul]Gh(?}G5c"^1'Xz_ElEl:{Ԇ'Z}͚>Tw g zdJ8pU=84:UM1ź7plIRG?Po+R"5T)i>.rE+&CI,@H(9htdbhgݨ ~)myKp7N G.kcψi?o l u_%A2ʹ̒Ĉ{oG)0tu1vm(E*WOJ kj~q6j!sbtbr J^  vGKVFěDM/044_;ͲږSyE&Y{hMyJ Glx/Q:,Qj3c$)IaǽuZ/q+Y,`3L=܍5?g66q ls{5&qEǮjl6:m]OS~kWI5D L&hskΈRK Eg9cygxVZ\CD$U@s"`1Q4f XK_^MsF3fG6M/Ӡ?oڽh`MNf\a9~.=74\^n] TPV#9?zc#M\~ĖNEslR RWغi#)=QP.HʝYvl.PV!ޡ'Fy:THAA|YFlmkUkxv>W`r _dڏ.HQ: ?_"0OgEiX]h_ b8vM]IA;ެLWGUi&Gu鎃HUP EOX]Z+Kg M4uDk@j,Y.?&nBde yB,{v v`x"@BT$G/ls7-8ۡU(A$*g*%]`}8]T궏uJB9okZM1+e*mY9OY;eITt0gh,q/Yq%u10 dTzH K)ݶS#76~HL78h,Z9eb0[i$(;ZwuR| >.D=h`BSKKCfؐ`݀T܏7 ]Yxo6.|nX_WRF/uʧ=4.wj5zcDncWW寙m [;||/7h]xBaݻ[t$mk#|C b 96/;y<~*zޡ"NU+iˇUbx7N) ٨N A(˸,?/qHF0IQkHÀ#! _8ƐTuREsqѹOLCuzSН#ýuU"i@Joȣ讚tQOujF2N\Bz[:utޮh.lV8ێ]}pJ*.+@[tMLaōx*MlH/%REExT'SFYƎM;ʇvw]plW4k MYY`4<ͣLS /@u/"yTYJi1؅‚`}qO'WH ~<+T5?Cұo%5m(f`C< ?^|wˮ1Um#H⌼:EϷrI538t#^x'"Kl ËZ@/\;+DQ@Oujb~!4&tp;Wu1שnii=d1[3gڗb7s?k3v5}Ǭqh,1(1f¡%9hd@`rDzO?kdǘgU<-2 a8#?ZP ѿU`pPsE,*qUX,s Ǩ1! VxܒCqm: EE6bÀ ~ߣ(>}=ƱWHTXA:Qu4{cno7އ}uGA,4tuɳ G>x5әl6"jܾLLeAz ('W~({"P0_7ZEx.M}Zh"]QS^~.OcvoGofgKQ+|6{zayþ$ξ(!J͝@w#AVIc%ʥ Qޛv1Z>u (<1(6*O*,Jd |Ko|$=@Gqn9#{k0 j Ğ9#Scӿzq43.6  m1rM0\Ks 56D|@-_:T kZ&ConOYM|jE6m*I5TEK  {I˂S/3y77z9(]4s`@aa7ܞ[ B*,FwM ̭ŢW߁a|T]D_`iDz.mP^w +'-U -<}ϮBx]Mw TaJI@*-ͩ)M1` ^-{qǰla1pQ)2ɟYn+HGX#?IoH𓇐u.Y*oڸ񠮘pRjF.s~xS$0ˆQd"@9E?)u[."]P5T6 f `y{0ˬUEE9~7QkkW_|%<ҙ: _"*K b}rř'm& \iT6w'^ku^o'UtPU6,F+/XQV^dY+bӐѳ (b?5hVyWJmB͛1OsG,㵵3H^5Gskf|_[)6/۸2O).nJPў_s5BY"Rj$ h ŻQH9}R!u)7[s6C_$Vv`REā-j@{iU5g'gdwdbFIfzzq/Ϛd5-dpdr\ŐHCKR͖O2.U>[vz] %?%KnlSac=88F3-sRLu >M2^ǮJBsY 1rUe]F0=/JvUbJJcj)Y* ۉͩS1JP(QpRM^s ,]I<_>ݙy0^ L<*=ԍa3 tdLA(Q-,pHU D*v#QÉX|\ YM'%q2Ξ P,bbǍNII8mTB؄o ߚaxO:m_#XۦI>(YQq!7!ZGJ]$Y-$xh*_8vjG=Eq'"ç~ sC@_$*8G7[38{A[~GEL5#Aq> MhJt>*0 QՏ{Ʊ7(C𒒪3CGwߋo7\t8(mEQ7I?'y]_(^ԅbST詹v֣-k XNm/ڷMfu:balO{~0NtiԳ6&i9phm-!D V9tՄ|Ek>iؽY&Ŝ eڜ.(+N1W,~π×zM0&xfqY5im0G"!'R gnlڏ5ᕄwۿP߲\?^?|'#w?>>Sw꾐9?&D᳥w?~r_ڗ?u/?vAпOW=Ggoy~-%_̿'Gߔ>2)g?~A{o)$^'l?Jum:=h:5ܠr7ZCFf<6H>-J~)Ԯ)_,ˮNqU:JZ;[TwCQ+`6{hz9M _zS٪'Gy v_ e@ aSd0?@e4Vw@sd5/O͍NqѲ-¼vmߵ n6ovȓY^-i'lٖӤ37ϾJYgA:C{G<_-nky鸇B}s4e;%oyi$GaUz)eFϫζ}Я Cr\K ^bdd-H}(:`PH+Ϊ=1+1x~ׯ!D{I%&1hWm#%7-8Z^]g)\Cg}>&A"<_콣L@;+D @5ryɉ3(1lo{?jf<=|cv& |)$3"$r%QoP!2#[aU L=A`ƺdrU[-땗b2(NK#^{h5_ 9ޤR{@'~t^ܿb ݩߪJbdc(,DqيI2%(pƹ-'#Cр]Gڇ | _Bp LU/~|ۢoF) zL OKor,n!8p$|LҦ-hӏLc6%R,RP>R[GmK0\AAʽwHOfA2L2Q]{φBO0CL9yU`2ب=Z]YUNO'I˙t.WQ"}$vҚ͢e)b%2"_w@R5b4oٝj( ЋK q^{D*=u$&\tL$b0ቭɎiarKwm7eL5[o"AoM\[eP~CllCScoF\ Hvۣjmv}k2"b*P$XWޭ@.">1,:cM~/(x2Dmy.`6cQpHCڢZ:y?L#=1x=g 퉹pyLpI$hoV%.dyٗ}2>Uɋ1AP=X #mJ)mEn􊩕8Ͱ9 Qu14}󙟊%$#q_J:.iț\,ivf~FLqc?PĬ5 %5頵%_#1 coǯ)[/?X$DЙ  UƊT9D< *mQ^k~x 03F9m˻VOa{Palœ0aRu&B I,>F*#̎5dw:sp 5k"Z[:`K;%]ݜl'!`Zu@]- |&(G. Ӂ /(Wy> DSS֜z,Nku-Hվ=3 O~;M%-S۵Q~L*wŢυD %#Tϯ'2&lO:VG }\bri?5̷kدviC7׵PsZ ʽ|oEƘ]IŚr2#ڴSVtT-84InE$5HF@ {SBu~:MuYϜ6YlyK04wl=a ! ppeS Z?-c+N HHZT|;Ū>9,PsLֿ:uw3n8vm&هeޅ΂Vb6('+s*A7D28bt-1` ?%#a]1}HQG歰'}:^@ A#|=ޖ01՝YC80I5ZԫZc̜Qbj`PئY9B#Ԫ̎5O H7A[ ᄑygjxW<=2BV=cwLF7l4R0ÛxcߠC\spX3)8]ݾ,m³y_ρKe6Ph%= ɵcL,SZ9^vgNwQv90qPΨx肭 ,V;r6E퉡Q^Œ=?%,_ b4.6*6|?UMu*dAn|NC+$I %1] 943YGԲ"$ v{""f-!vonS+s)6%n'ʱNZfTa0fzcovD`buFh`]n@8^~^&AR6Mi :kD5?Z4\W&9i/`b0,rwuf|}(JQ!hϑ7pA$Kˢ!APcG2cT fMQXM{-Fn+MGz%cHv^fjj{Vix0U{.T Xv"I%sP{Ifb5(Y(U3f 3yzd/``ߙw=yڨg Ma[ g?(O]hԀ0q嗹Ѭ>e.+KY,h0e'8߆LȟKd00TR<XUMc 1o8zVaܬj`g&״l"?-״71%>.@g')G WTbh5pfErYOQ2.GqÕGAKyI?ky[`?9fϙCE ' Ω?t ;&ǺP~J^pY.+参15Įlt:ήrZW^4,_=4Foũ ~Wpc!ue/b8Wx䕳6l Ji"mdsW"_rI{_i@9>X1 澎/]l023Re(XH]%]pv=v>0Rr0I kpc&u9]5 )[?bp+&)ډGZjۺɶuqCGj<ҁZ/="༄L̑F {fpw+(Li#Wc:&b,rQ)@OG$1pX?TQ ]Z[%O:Evd@R]= toH' &@ ÎBCsx: =W72-DV;u 4:@mœ W&O-F^ 89ElS}@ 4Ík[e ۆҼc@S[j)9࿴0Eo9A kiQ&|E=T5\5r iO>1N.؃haސ{+>C 3F㧞D/x|8v檚!W686ŭ }GrZCaZ?jLb"OK_+)TޕuHϩa= kZ3zjGYſ:ˊ swoxuƝT]6wvPrr=b;tN+R }z;Ĉz?TʃTp8^E)" <BIrsA8INUxXӀ3.vf7BRczo x0G[{3r!ڱٞ[ KjQ3p45\2Kd6QKW6*ZωrMБmOK{_XKuବx;GcZ. Sqc1K@Shx?^Br]S7Ѿ0`AG}Po/> d3΄=%ӔqkJ'p2m4oZ_H4g6pth3i$\6 MbtQਁBQMB{>(6H-z[}:t@;R"4ﹳŖe\޽'ztD` -[ N-ld+MHܰK#4_ހk1kM6@@|YazS#f{1!Oi/ԗZ|{wVJr864]ڽ'-/Sڥ1auV de9TR[UQP=HT&B @8fVx@4H#M$Xɇ`m̺m!f\8$y0I2qAbw"kW8`Gap!RZ\WG.^ofj:| >k\ZuroanC߃JqRQPz/2yBdOtAy'tavɷʿJ-6/㸦PS/JAEXL(a&s)C(_ݞvg $1"9{JrxdhtU~ejcc{L["6= X,:MD[Ɵdbs9ќoZ0:ۯ0!GsKouWWt[@[ģy i_#N#F.OjCR޴eb8xG1Idw3Y7y/[YDž u*K4BSg9.48IaAs~j-*=ǜ BȜur-j#al1g-h('VT! Xn<>tHׯFt!Ο}ՌZ13'1 r!mM:G;";J̍Uk`65(@n {kfݿ8&kپ@x2X?u%{WvÁ lDaZ&֔X=kGؚoh5iü꫽4}Ƴ6ឿkl6BBm =ѬtDlOK^{AX.T}xg҉VsZ>2,`00PZ5eŬafʳgC\W1_TkŒ3VF;aW )\HK)لլi^beQ:K' L$8^W }=B4\@ҝcT.ėW$ycaUj 0w9cybX)O:G<ꉵMv=~z*F%[ J7r9И*wq5]1Yw^8}y)?P*gkrSU%P+浔p,TJO6Wj VĪlDI vk5 &1;ay&jxu՝aa֏\k=/ČOpT7z9Oc }E96&?p%ˏ~XQOJ*AnVd@>;i,4uCفtt)Bo>]1_DrȢfӴd<&O)p^?]eo`ucLHVc~`Do)ŗD/R~ȓW&.PO%neG57wj7}<:IAç$ޒTUty(wy kc~>Eߦ֍݃EH#|[]|U?DDGv @C/}pay!京Ů-sѡϋqɩT]<H @T6OɐYsb(._fxUa:o^ (bې Hceccomp-4.0/docs/images/trace_pid.webp000066400000000000000000000750401514205130000177710ustar00rootroot00000000000000RIFFzWEBPVP8 zP}*>m.F"!*Ӽ en`toc4OҦzG?{}?G؏^}"3]S/o?{Ms_??=\%WP]d'3C'?|l6Y|.wo?]/???cmѐBF'S冁¥DT(AӯJ+k0?QSTvnQc _r, |%q9+R6_BvbXML!S'>7 ðҴeW@;z:oHDa<KGۍ Fj6Q` yE8rSKߑ2$-9LHekdd=fՇ)Ol9*/=}u_j02@"ݗ>lpyL^ep&M~FO)67L l+tIUm `fCM%#7%}u_k]W 6ɇYLl`OJ" nrHpv!F= iYW\#'Xs`z%k!pL׼G~,r^w+ fUC=dx%nao`Ys.10/:  aנg^v+oWʚ5y}5xb~^C\(.^F^L-/ ՎY<Vҳz?|fVΞ-"nyw;_!Oinuzٿ[l"dxGN% !&UJu-ɯzPߊߨ*GHiFe{8cz!'zYA*ʽDJOQ6I&&[$lH;LJR@ ]OmX% BuǵM*ާ$ZM yAD,|n;@]^ mn]OqUq꒸4- ,3*8( :ƺk#9$9*Eo67_y+WP/vetY/l>G W'R[Ibr:>w ʒ#ΧDxbcvzFCMW{wGhjJֺ"tj W>Ҽ*}"(n1F,4jXe]sۭG䫊NT;guStb_.*!4aoGLg  5Z[Ji?L++"R ww4~[@ӄ ߁8!GSXa[Q6p~A2C]' ^*O_sܵ=iR 0cY5|3 Ȭ_^QH1P^zJ1Tx7b˃,S$\MQ޾~ȕ~jn]HERY73<}EaFLEPqk3q)aZ4no߄~S*OY8*f͙/so3ZrC^jxIٞ d0|%&h4.pF SP3V6\_ݙ Q?0ۖKMX^^+Gn> dj6K.s;sQC3H"=SM8U!taӃb,b8AyAay\ VpBR/d 'ζ=uxK}ee4%Ck!7 S.(7Hl^,Dw _$?f-8<2Dx&Rf|E u(OJtg0=&k _ͷKj{knB)tp/pqNXOaCWCD* +yuId\-a^_\e;#;'VPȎA0mAYz$p?ez.B&BQF5Q.᠛泫aDtqwtUϘw&1N.'ru#-k"DTG*5/aZ-)ьzͮudcg:yV{!ʀԓ\7'%nk1axGnNfdi~ҹGd e)U!O L&JkA,aK$MPpwI .g,1!ʤ"fMH41:\ۯ%wzoe &|ŅcI3s4/C4y 2yHّgJ&ƍۤPcѥ.*eK&NesTYOv77fUo+udëtG&{x`L J:W~cx ?S )Q%} ~";K'tyL!ghJ⏠b]"rdղyFkzt%Ia靎;.J)JGˎE/0y6Z@/qfF@+/:cz&޹B@x |(7|жq:fU-u\ ZĽcgdz?Tש9v +ﻃQαR0aF库~_޸8;6)8]؃_@9-.  kՅg }L\;TN4j!+P=1\tLѣdht9-22d4桢Mb>| 0^-bL1?IR&ȶ&9l:S^΃P*?9_{ R~*EYvG+7"؍ϵ1XK{w.}2py#UN`]|Ǽլ/^$EPpew&ST( C ۯ~pH5`lViޠ=sM-s~ڌ~g> 1U#h'xc"sʏ:ЇXBp7+ ZGӔ9ime 4R {B nlUY!wvXJƏ.3FLu #h[7⨿ 'ٖq܆h=R! +Iɚ*;ew/՘z}Q]dU/;.1Wg))ǻZ1|θt'0蒷[0#I&Pȗj/k캦/Of#E6揅G/)Inr+;U^ dkYӺVbgSH0}U2';bxWVR7Wn(FcOYF"u|0_Gp:q*$˜NE 0@uPNi%?lE~CoZ5(A׋OܹG(Tb+Č!\4v!Za|R߰"ԏ>SJ^H:AԱp*j=GMRQW4~1O;N.Cc W0zO(FgYwNcNi7}FYR;-Rg(. G@J@`Y{#q7|д eDl諒&C!}f)]鱿o$Q7pmgzrQ !194Ig䖧=q*w PN8q@e4Rz]AC\](ME/rNO ϰjw6 z4ZARح)KJ Q)FicxF4ohoc lf#|ؕ2n_8m,ng{f (|#+s-r.ӀkH:$ m#;b v*uK쟭ɲD}xPre~a[xRy9^~V ll>KYk\xIz_6<}?^}Mt?@j.M1XT+k4EMB7b{,dkϺ2b>YyC$D_ T3m6\3׌dZ}{jٺ@jnf78u 4%|u6D J:YA {G3$.W<|!szN]z,XACY|Gɂmة!ߊikNu:m(xcMKER&2H⍸yB "#ͱ) íцn{,=i$v8e+eR+W>qD-:(iE%vYԢhWjC0gfj Zu25LH6B;` sM^ۻjmۑhڸԲ8CΡ"H];>M` 1Ѣz$8B/$íAr x;5ogF(v"9B4J34@1ꡇ]ak=lN%dq9Q[`:>wȪuM[*֗\',^3-\j|=Yn>K%ܙfߜu<Ꭴ^\dN}|LH@')F~[ X/_{Q(Tr~c7rK7DW!7z/[Ƹ&hrMRG FJ8i KA}oƢioF@-M?nsqρקּf'T$fDibZ0Ϥ7q6J5tVs!J% dIR]U'&C1u*L^ 0\6:m/uޝ K+d HNpZN0<l%9!CkVu{KÏ/k^at<`^e=Bv*5N"[$h4CEQK|dBUDtGks ֩b@sҩAQZV&~(;rbX r/̕TWhԇqZk 2PXLfZ>qUgW-vPslK? - '5ߖVڿtt>XyѲIq"/.jHQE_%[xanݥ;(Xm3!tE] &-YL˴omcPoGh-P#`LJ5LJt>9Pt#ixjkqA}_ɟL&Z}i?ʔ5T:+,{ ˑ@re:E$Fĥe0[} ]vaH"YzNAOlJ: g`)[NjUn-re7tkQ^VݟJ1Ion U߳m6(]* \ !1e=@sx3p!j&c3Y,4ǁQbxoR*P|Eƒ YY |&oܸ(g ~<p=pΈ&1"1+S ;HQ`er/Zjw0-G@cf$C?sG㷾<:)T)iQjp x?ޓ풑bhxW{W QffG6>/A{E_Ym=I坥Ϋjpo5W9:wC.UJv;Iѩ ABo:5q~őVffd~a 7l2Ҝ\B7H4>*A,\-7k|Sd@c"vJvZHrG]-!R͒΋.&nǑLUbgc=!*#Yp^~~Fںt (+hjҹ]hdR9(s?dOw8Q2j;]@u_ ͅfkb%I,vX-sF^ ~JT2n.bIT`J|WV<6hkŒ|.E|Wx(X#5EyZX.즅p>Odri8M\W_Bh QJaĪ.hqƖ0^7wPI\a4>^j_%BM|E`]̄*8MR2['_>2jm~0+mAME:V::O^AFJ9>l;: %/sʲKcNxc>073zf "ةq{ZTYD]DweڕU!:% |AHgYȶ՞^4Ј&u KD/ߌ>)'srrtMsv}%r(D2 ^2vkԋ o@ MŚg\C4@Q@ QS7M\} pAL3@(3ki o,$ҩ|Mu"?@g ՚vCL.(,"Pk 404}_P3x3V;3IcsÂ5AjJ9y5Ոc.=Щ%)3 M"0q.h}<-$p.RU21d-yC\m[v*r`R/br vѝ1!/h$XgfZk^jP)Z{ teqb_gr }M<e*m"éލ+'>5֮M?MQtKSf{GOMN*-ŇfXPDYDufMٴn#CZN#l0W R=ؤב8wk龲ËaZh$]RS W6aڥg_%t3Q͞& |s~ @zPr@Wznj<\\ a\Gpb܇Ih*Vn;re/f^ :hfMCr^@iՆZ&w7BbcgICm?=[ygr+,3ֵ7XP'vHNԛ_!-kd^(&OCɱU>h[BE,;\SXDU5_Ɲ?2vr= q ?J ϸ0/: JugǻԷ̍-()2<# RNhZϑy^eǷXl4BPkbNuespե$ӄG}}g#&ٳC,*2ULgIQ?^.xe1GQO1/@hhbc@^~?NPSNwYT *?b;.C_  lq@|wTa(F^ÊgdULNp0Sgそ|e!KE6]rX8K%z$=h$1LbKDO;s'c1Cb+VC;[AGW|-)x䬑^d~UFb45Zg4c,w4βpHӟ/.MF%<`wcu=t H=9U {ݧ Ĩ=RWMje=M"yĆ^*&N҅򄓷Ç /vn5m3=8S}S{օ2$;ɀP%9u @~[^1tL0.a2Q+r`~9&QgRW>y^)62N1re\eVė6w k#s~[™vvi)iT@,J\{Xuv.%]zQETU/Z[q96eρv59Pѱ cJ79_Z)s3\E8?n:J|PPCkhd:c*i7ts܍j,~`5|2L4O=QS^APld4 C/FuܾܶݓZ {̘%B:“g`09!rF| Ƌ0j$8 w6oug0Ab 2-c~*5$*?L)U\ՃD]GްWJ{O} Tv@S0~ܢ[R7VEr1BTyo`^?Hf)$ǵiBd/MMBe5#Er#X+_&g4Qv0+Gt/sdFN \7~Cw0UQkFbhPʲA_C_#$LBFR #>8:%k6јv]d-ZnMܚ ;+taFO %'υ8f3`ȭ xAԳ(ry%Y2(鄩%+*$o cR+p7!\Gt}z(ʛ`$8 U+\*{6J}'z\%-n(>֍W\X!}@=% @ O>] ߸ 4=2ɪl>-3A|AAO0 4z>kCM#8LحsIӪZ$їfm8x'"9/L3m[ M~d~ 0o3[)85[8lRˢtCT䢲]\E1(i^='PŁ2VɍN;6oLz5Ӵ_COsưjvHj)ɯ [pj@mH?RJL]yNeDQ^ UGo>%fR!!㼙$'eL뭽I(š.?T(g }&T$|5_ٹH0uU Na =7DVN!#(."p ;C8 ܧ=I/ZXiwpv\!(?(b[>c$BXON EMogpAr9Ir !wN>[%QpqDjڀxAtC>:a; םO+n8hsgtJ/77c[绖J+7sNIһ<ÑQf[ m{: 3+>i `+[`_#mdeqe o/ rEDx̟6'Mh[%l0h ձ1ex@<`| >i|Zz[ߵ4>\t}7~5uK@ogyRō~6,lwOW<=k+Ѷ!Q׳K(֕lc.񿎱e:l4^ r'A]%;M Hfm:=  SsJEi(O'JilG'è^>" GXY<,q;7Fۄ*$Qӯ47#Z9 9Otl{#f&x31d#:k6Gٽ:W%#b ރvT>ry%QQ&NĠã3_bf(/(JLLT容K~0+Ixso4.u|Dd%e~K4Q{(=I]6_#nnGo*7*(Pz ʗfB;99;xzo_DeSXT$N=Hg:DrkHyG!pŁeڴ$ډm?؂ϐPb6UpDc WWmfgX>(2B'xƌINt F~ާQicS82ϗy5;Yg)s8fB"9w k46F h$"ƴ8&^\r!@d|SƎ]v4ML@'F-@-\.j 3Bm:-G]?}AqU;z8:xrjcrQ'iw >`,qI+P: &EakqdS'6nx0%EJ0 53;Azr%T QA LaҮfwܩhp7a2#:,HZA8w@Y1P+ro8vHS&/\`=r)Bl\2Ԯ6)J r>2Zz:Eծ~'t_k'! ^,w2mQԅr*T$ϾZt䕗vׅGXK))0|D>١ i>@قTA*/[H=q kD6v~f}hjX6v^; Z3I\t8%~@jxp1L\@t' R3J$‡!xYWx\R$BBet51f[HF.FּLh&a:RP&j$NLj0P⠚ɘ$8 JSL,G.yu=Dܒ][?:B:` @QhXY{ VzG3Iy^O3*]q7~ӫ+,̓2Bʗ6=J1+\BC]>?]'FN. "%C~+zO>V'v+ϛv ,,(Dm+2 |50]/@]Ns4`YU`hYp6gnRCMeK\su}~%N\ҖΤe! 뺢/bٌ6Nt/U&x?K 027WaS#b|i`{( Mjv45VmA^d!q> ZL-f$%C |G\(Pj=kRq*讶Wht/kpc~>x>5sጪŔ7EZj3^qaj7QG{8w":UӝA)}CʚLG}W]5u.c=Y,)YҀe,` bTT`oyz6LRmʉP$뤓Z3Ȅv;6'IIfb5SܰyY)tXDЊx\ky`ωSNgȇ=VJў#PEFW19[`%=#U7uWj\)ސ6p`S^@?k? Rtse|]^]xԇU>Uh]Ltp),CB?OHh] GŎs% 9MD/=L#[ԳpŴ~@)WF6Ӝ cN@X.uXV]D/osj* X:R7IlWlE9lG8t̷ ʤ~(;/^M7CE-/x48h;M|3YY3qX! y<cEPg R3#〸QJj<# "C]\~XϓNhxc 3`pq@A!7 H{Ӱ-{[~WXVajOe݁h!T=G8OR';yُ)H@&{qj\RV"vO,Ȧ]P!cAށpBr޸"̃4TGco""t/ "Ib?,(=& *y(cRP b8SC2)+oƤmfl]ϝl]nLaeؒjb!:^ p@zّԧ%M>IRBH U_#kMW w:rg/USN\T.Ӧ}(;jjeAkI{Pvۜ'P_rT5w8JS^~(7{Е[k&)Eza$>W{ X`+_|l8:Y?^ڕT T*q.Ce!ffΒVS@e[xTUvB6Ddٷ/c֒~AW\'UidwR Vl9fAPWsNn@/S= Z~LO:ⴤ\CɌ' br2Uؤ<áDǢ;EaၫfX@7uR+%ذ*|fq!;Om6'-[0Jx GopYYko@jÅXATn-MhBoXmҀF H Ep˹@TګjJLR^ LWߗ@܂:  w:56M/Oa/)Xpf7|oF4̕}(&b 1hrC> ;<M㝆FR:żog1򑳏7+eKp*S!A0}ؒ1we\VVxZXY,ѴеDQfI*P}e?7)el{᡿w"%7Fis=%KWTcD)JЪiH5QFawXnP{> x0Wö-*=s+9%_/!$6Bbcg Jc11M⢟P`ʻءrЯ7cٺ \2 F0%XoasKhv.L 3MӖtJ4:XF+qH 6ImJX[A* Ta JtAsN<"*v`0A%T+7X|mp%$,}]G~2}!pLjJ}oJѥuU?#|PZ|2ȬxpY-$-fixDBcp0^.w?$ A_΀:~Ou TB.;/T"Z ]_>8_4ڂ @c(>-hlC{Zon NG>KN;cI4KFti"-qjj ϗEq8[})Ċ$1<0kBp"5OqژWo9E`EkV<xd%iNVܔwό^/s-z10֨"ϣ¥u0T">J$dѹOVM){JYQ5R}H,u UV Q8FD^!>LrHjg eώˇ \3#03a$ҙi)cN@KÆYylEmur\9:%D cr=$Aԣ?-)V QE-8 J5Ay)<9VBsP#?7E R(!lmbW 9[0 7@"ld`Š jPQR "_!3RЙEo,)'<doD/e@m,@ 3_OIlJv>f |2rJzdiGユQ@B'QA;DKgaFCJ7{>Cv/bT84 `/vEڳ 0lo=b WjT w0\K\}sUq9mM _ߡ_&=ZgD<ҠKɾ|̨~׳rpOHܬr6T3X%3,g}ҫ`_Lqr&r_#HG8}5q"y%KFUfD R/YSx=rc%r'ܷ? p/t& Yڠd.n<+;cHxE ` 6]nKVS6zVDz}d윋=!:_ xFUf0$A$/Ly:Bq0 dr шk.45Y{>eUc$4' ώUTGEX Ɣ׊dЊ-p㒩1z_)y\dP_{jxCu5_V;T}ݑ~jg4!,v/Cg|BT?CKAS4VH[p AKe O`p z󵉍SCb:>6)iTr{kb#hFI!{ xl~ͯF2VsC 6V 8B Mk̃vК苧}uosA<?gYx|4htEWTł;  uۊju+fT3ގn B_G> ( e9ZC$ژ%s/ JJXƽ=C2TMauemCn\fcbݖVtz6!cEfcQ ;J攵.It&)@(p*a* vO(v_[&bYy1juzؒ@(./~k݆9*PD'5|DuVlԧ>X56`rbEM5'|6AtW*0vq߱AL.Ț~7 #.6'Fqa6CCw|k(' Y-mؙܜj? .}¤F LJR(N:Iz.6b?X=ɾt@n@`݇WTfz"ҐA-Oyfe'Z3KؙPv_Z,cnDgl WS% hF uE `yEJN0={MR NQ58s3q鋑C<ޒ\]8q1eiziX">(fZ 'pq-G7ag̭C/pg:ѓb*WpLkx[&ɴ>'{TR,6Xwbڅmz\օ5:!*g|X{\J}RY/Yj\>a׿^8^'ArSF]!'%_SNx u}vce.~>k6-lJt.fW <4{u  }AoImszȣ|Hs%Mvn&ieU@QٔvFPeԁcu@7Yty}o1\ :$Y(HfЪsvahȞ{%rE0\=}\ND9/,ڟ|ZhC}^ef.91h҂_O$S k86ZD <R;g] )l'qἺE?,Ynv觳|a AOԀkvl9ڝ|%QbwiR//u0b\K(Z!pX.@ M U\0=+BEй uϩօ.\~t2s׶T@w`ŭ<#scֱ8WM*C,`fhw刪C[=9"Aߠz1̚bi]ÌIbHw}AK :}`6Nh|:4"W{MkZtl%Z5Ej3F _Cm w.7-ꌙbsb( `E2rtG{gzB@J:ZFh1Yᯅt47TOKyh 3 2q~ )=a,QY(a(V2C =I] n xΗ>AE.;UHSxD6yq|ɛ&%Y8%=|[j.tסB 0#O>!Yb-2Ъ3('#?9ZF3lipiQDŕ oW&]"Tktu^sBK] %XU;Q$]g^Ѡ -yT``xoGHԪҩIjf~)Q 5v1Y&{N|'džM)V0 +0k+ـ IO/_V)9 0 ڈ瘌{d+Y߮&'siVReSs,"«n-3ڐPC'd+>7Hh>c%uTO",7kQ;p{7ztZG+kja(-eн>aw_[n{K0|Ph;a0> S ~czo<"88aZبxU;ׄ*5#Nqq\bg/#pQ|U5 bw"zg!/xl)#{?pZ#ޚz7u`B" &>;8ƭ_17d׷Pٗn'3wrG 3aj5?uȪK&λC[Q2Wc+Hu!aR*TL@Yh)rA;В?ޖg5@TCc׃&ܝGʹWEK, R@BYb>O;X6Q@T< se'ġ9(jk0'{*.,Jr%"_Kü|ooNYH`4IJ鹨y} `aŶLB[ /:<˸hy7"%4 ZՎ+r˩q_b!@U?QwtX0Jdzay75k.qqbg,:KUm.?~ar␝B3yprZд]skpl-s&x?U0Tߡ |FMu +оQ!Ǣ§Ǎk!FxP?e=1$o3p@2Zp+C(IG1xdX,I `1G=@nr-pˌO\K3[ $%@`pd2hD`FƟͭsVI rLnAMȩبi^2U% 6Ndq?X:PՃ09y \kZy|X*s ?Q!(f_ cbIaDԺ~UȮyW G%xȕg_ϟ5"UG+1B\Qy]=f_gp )[7d[a%{*wҔ˯kcwS0##tP=:Pɋh.3V 5yPJ3E|ly싿ɉUM T&]hJ}~}dףFriӟwyB-KR>-yycb>z wP]qg~ΈW񛇘'\#{e 2a<#yuRBFrdMK?\e~!L|4Oh2Dqf`pӎ(Ѐ6[A! 8X6RX[+PQn^"iu˭CZɬK>ٰ_b~i$)8!'EfMIFoP]ΓWVOx",ϲӈV56~,ᕋ'[I ~IE\81Bd`KKSF}ObݾAMj335B+hk,p&xPó/&^V#j8.c}/UpVz( Yh`uah<" dXxh-!ɓW#RBhqS!O"|C/n.y@@tb#ۻ6a]͍lZm8CG۪%d.p35'AIKy#J,Fk lcP۷$Fc2;,3KniQIa3-h ljU T:qϸ0_ rzk! c5u:&Ng7no&G7 : B}om^|%sdy2l~4h4r]6м9Bz99x=u dXV0PUP`>5 T̜3qe 23':ym\X%Y-%v@U9|R(Efw\_Mm7.T@Z](.= $0}ekHZNg/ӯ!\tWj^l3hK$BɣYK}P,moEDn~:#̖ #9I \a0Q$Qc/ %%.J]in-*]Y Cݸm۰`Dg$ںdJH8޸YZIe`"1u ņ}~4xou;EZ]*N2}禥%SʄiiN8^?(4 W+=eBO FuoA,K+WCv\S J;Ȓ@#-Z(֯5J Z]7,f3$ДӠ9isy6ceccomp-4.0/docs/images/trace_seize.webp000066400000000000000000000723101514205130000203310ustar00rootroot00000000000000RIFFtWEBPVP8 t*>m0G$"!(< enT3x_x?}7t;5/+g OӾ?z'P_?~{_//k{a x׶~O_f?~S̿n[_?_{O¿?2"SO0#_ #~I}7>=o?5s_ٿEc'/ [/_?"?:p,VthSvNS3)Q Qm>+>VH)t$ibH ku~4P֓BEBj?ԟJ\iW:_xM6]$(ݰn(K\taGZE3_įgO},4$+dMc$M6K6NOGC%83Ah[yD yv/iq(tu@ Cqz3\pN}>ήbpFf<ŭQPNF^.+M A6zno !+˔kKZ0ߤC2YkIN+)l%ɢn J{E`f&܃1fVjf5EB~="]cI6Eđ*rvJZn˚ ⦳|ILA3M%ь&\AC]a " ay3UrҠTǜ 3^ټ*?Id{s|w0t\wӰszw+1S=CGX'xssQ8^+p^qpOy[B$ (~.΀XLc/=-y31-jsHBZVihpg/K65yƐ]"Vg^ZؠR\XǙ ;љ1kTSﭮ\St#\h ǰ>28̾%QwN=ꆘz(X4<"l͋;ԔB*ıl @|zVچ+qBW-8h'F]E|םB5>zm[]=-[lEr-TWD_fkT7#:KA֓I!;pkhn LVR$L'Y,Dumؿ?m# Hi`!8 7! ?𮅸" 8șWBidK!0ZHn@`ّ3ntUU҃l?_cJbrCtf]Fc`EvIaysl^͐ƶ&5Wa*L`R$NmIA3&\aй[VxKc#GWM$rc}[ hcaߜ>[ ,QlW@J ASn"eC*k_׀x 8:50+*)B_^X??+rU.lW2,VAoC SR?TT;1;%7X$Uvd:' rk^pCp\uD#64KyJRRǨ.'F7H1(j YYȉ _]F#OR5N$kpD[52DY!-3_Uc ģ D; 錵D{ )s^Ќݿ@ *uٸ4k|7<8~#=mrNp\4Q0>GꝄɰR6C |w/i<ב;=oJQzN"J-"V%;%r 'N#GBQєWnΒ輧@KD:f옟GJ;qo"R=h )@0F7׺?hRUۄS8oߋh9 =^S80Y[fqw`Lq<y2[*LJJuW%F";C_%-D:Iڽ$>S]Z ?9݈@yZ2֦=5ݡ&WC}@;S3_NɈ`yz2@V6-<1AN7mepD]ú*I%<˿(7~`8pMcM`i61WwvV?veu䏂Gtk>iVJ&p3(փQNTp"[kM_$Qٻq8fT%ЄkF$X܍,+{!y|?:e CC=}UOK;D)oF=,3M6|-w)ʍ`$i!DUɺLG Uz`ϲaqvƏvol}cI:M4fr6Uz{[L=VB9>N:2.4\2%t@9|Z[Zo߯ZPU s j03hi.Y09OU]E Vla5a=w{=Vk^53ULIuwG?k}M蜲|~!F$'Tgl74{q$\hEZhp6&40$VxC1]HK+efB+*iZOa~fZ4`'ʮv!<-7i"l>m!Q3Fyx4Ot Fkw'8"CY<"h0t$E#S&CRعB] Ig[b9%gY丠auPӛEI2oA[ApO /"- 5ƽ+/zd:MfECTg"CD3R=ѾBoϬVgvV;[#A6ohyA8 .͝"<Hʿ>3&q(ҤىyWWz'! t?m1eP,סq_^~:;l* A'z\d qR6VYU\{`SnJ@Og/C{yl%x41Nom̂YcJڐ:nP{C ŠGoMrQL3HZJMpĄʀˬ z/yyjTz5rx VdKU:6ں\:Jj/W &qIon!y,|S u dz:Oҝ3l&IJdp+E8\iaHGO#u_%CTu65o؝XlkZSGik YD0ruZGjV/^\1RnK]YFfEYarS;￾ qVJ*p0LCⰴ S] y wUR=r`Mu42Eؘd&ݼ)W$c=DePc!|D%2%[or~пG(XX4VЃR㧷Fx٠4R4/emyU*ژ9lթg < "8;s|(d,s{SȎfG*UF1RaPg k?.a8k]@Y4JqTÿFi&(#zYv&> Xi?& hN¥!*>Y[?(;(0꽇#\Uȳ9lpuq2g QTZaKO>( sϛw Rsf(Qz"EJTALs ݸ"vފ /( St'y]΃,BAcch8}< Yu^tkhW"dҏT#Ϫy@pTr~;~YVeA+-P2@(UI^?8 ^!\8ɯ5әA3%pLjȣY?ΐܷ$=Ր@C;`!eL08պlMG,R;jZ-,r fo{=8袛/>Jbht#kCpc`t#sPAފjXNn(kD*wXێB LЖ # ࣰnh$##4mPWSnz݈Y:]7% {}-ivz% iO[c1HDn?RKO\'Htӧ,Z} #?y7uh'%D } ci+J,2*,J6f^س3T7bg L e)ei.}L;9>VIP!ћM'[awh@#!дD)!)PKP{qHM) Zf33eb(EhmN2B3M0 :&D)fB(g萃rx=1y<\Q^MV'~n7x8(G$_F!@RsՄ}$QMj+22/ ݰۋ)60l MQ3)Dfi?ShY5N}"蕑h, ĝ%ɯ-)8QFY6!mG#o!|S2yW7 Yis8ח/=bN@~z2}@z.Cwa?k^fR@W ^6 2 M3m~WqW E#kE*j,,g{Kr&2Xot\#㗀5`ΈN[!s`E`U`G''2t&^8vUG ё-m1Vs0+  Xz:3R[P願V99POCЉz5-t΍*GwN͖_p0}"Tn^bdCzhb_d12= b~qE̯#NV 8m!|9v6>K'~Iy  VSDy"8k og`~Gx= $:60Jm]ݤKt Yׅ Zp-]0wt=a7eaЊ.:Yå: Xgݜܻ[%5byXzjڞ/,;L͛a`պO 5k۬%ODTfVnCL/ud3|ێ˛dF4-$uU27Dx,r%4)H;l:O,5c׋旜{iC,"N!F ͟4sTƁ@CTx-3hei79wGetmMwŜrZrE08ya:gBǀ hFOٟ%$B\w "f O1klɪdʳA IiW>. }T/K{Ire lm6st<'q颅rհ'aDez8񠨿[EH0?#cppK&Iv (zou ۢG_#x;0IZbOHDǩO Pאt~oMwagA)([$h}Ɋn6)S0*Y)18}#B,^}M(=a0|I[arS9}v۷ dsp!44P]7Z ;04ɽhA+M,mݤ \[iI8mt\&hNhV>}"dx6_,ǔ0^\z|fuWXPJ(E/U(O m{]5FYGW` #S6fk!B ʽt;|'zVVV՜\qpތUqFX6̇Mdsb&5p,(5!٘{Q]lнhܟ!44^4Qfj7Xחȷv3対~.-[W)@9B]ӡý~ p# ֤ڧL2Þ]]hZ/{^D}_F^%cM͕x  zx4|cmviGDgU1 :}5[}H>Sf?dc,b{(pZ\ˡV!^4hܠcEPxUޓoǾ$I(ꑓ9'SwmHqU<1Ӱ"'tDDO!gw&Ӳ^ܜ_X(6PakgWکB>]HhQ߻ m!`${BG4$;uzM1ZIZmh"eD$L?m!1?ᴸ`[Ca~]W齙 `tM<׾'.@6ė.%Ku[er nZB{@A Ҟ c¬dlov.~733fj)C[xyNT#5~`ȝ^ep%qd)eV9#Na\ @Cʧ`E& ?$I"Mx}l-e+﯈WuO&1wEi0113 %w}`s /]9#z-͒lg&])V F6hC^\{ ¶\6qh3^($*y$De⭚46$@H6ágh-% [zLf, DtDiL)w `}9 8N2wG=d_(shM^28ɝ]l]#W , A HZK0^~\}[s9׃Q.RO5&:%Od31,o!L)s *~G&"p5*CxhG;@N>܃d^\IDzֵz=tO5pq-)*],/PU -!A=UqH;4W|4Z(& kWL|<#OUrQj g?GkYAKc8j7+WjnB$V,cw3 QgZ}ZpEu^&|]vgA%8K6uT..YO ѩ {>.K27|]qr`RrZfY<~OwyV"+쫜' iN=YC[A!ݞ?Zk{}"RQd#Eb\ :3]ͷ8kT*R2oaF_īаS,:|H@]}?ZY]83/, ҳv%too~by$d_1zK\[&ݔKڦ5W~BY`ߋƶ -9}RR4/'5`>Pr]hLif@+rγ|BcGnݦgu]~E ! $G' ,]pSX&'u-G :SPfíF65.s7iv΀]y]>g<^ V@D! P.R9n*A$G7۞rxְ6q&iT]/}l{mBϳb:p5ON W쳝 -` /N$U`aFWz!ya.;u|~ kO6@ԈaX[n6X%7B,JFء_違C~2Z56^pkN++Lsm;x1_Nv.̴90[Pl*d Lo#/۪_W 2 Ju|*V&g>@KRVh5)P 8X-8OLkvH|~MkC 9bVEθ˺ A}VM3e26zepV 40ikdlu4Υzj)>HV}#6 1rTd3QVԈœP/\}:aJaW{?ns͂ɉ]2FC"4Vi"- ZyHlmyͦa- rÈR@0 R*=\Y _I(y1*@vQKa gtf*|b\/ Ej0@ߞY 6XԌ}t>K¥ H(SVϏK/8./.P $Բ#Iuz7D{|>wKsTP@˦pB % qi '[[^Fdc #ʳ"'"!9R=tCu ?'y-$ aqND;!3掟b΋cߝ˗jE k7kqCù&*êv@(&p;WOV1ai .xJP0Znv%s W&pgǸlURr@% *O+K򹰕;B-g-^I2GA<tWX7kbH)SCW="Z+~͔"@K%9]疍,e8ϳkd^2Cftwk"3ܡR4y~@6 FV]39C3Y7B.8}^măOXR ^$eZ2 v Y:{6 ĒYB,,̌NfNbDI[//o]iP(A6o*$fe|@Ѧ O*4' :,\rʒV }e-Dԓ1=BJ^ B| ɶy~B.oÊ(oAѵ*,ʧ=MrD٠yߜ6Eu(I9E:-CPME<֥67V0#TXJ>)m[kv6 9oj@Rs; t0찧1jcJ37N%E9 #3sӦ1sZ dɌU7pfP=WiFr-!*WKr2~qj-ؚTnKM/ XN  a(0#N'W5qPLĹͮ{%uNWѨlVvb5򾈴ԘFҝ䓪I絔u _X@XC.g6)WHҰ9xx( r;G1ؓŇ 0L25iJn= w@=b+ю!&7ޔ˓~Nֱ-kfswcT3^07QSSw#O[\"^c*NFrB ~5bD{kLiөbHf¹0K=-o"?ª|Xj,\ɼ_Lb}L+H%H*0$+" Ą p$w{`ɜںO\N%|D뷠 MV]ɠ28 O+ HN.ܜdN&D{J84'H w+=@<brw(t$F71"KX{49R{{d\/4+|nDkzvvF?Un7zV"gp9;pW ]c&jRN< we9 B C/폭' /yPS1nrVo]d= ap.mV3GqH-ސ.v5)'XΞ|+acEPA\tv-&Ь*\FeiW`6VJqw~o+Xw9<5=4)J\G{l58$IwS>xX vl h]P;.w .6%=8?8P.)$cί3F=i5t4))0IKU\G07?7R՛Pgw @~Qq(26ĈHFLwt * Vev{vVwn\8W7U/vm=HL[Wn8+LONɶhn|n='$: iG:{HCrrѐmzqಮj톲EҚ^O `FO/ǰ4A* {[i"|;H"kAkz֡3 |4R >S^'!ds$x<}5j]]UJru3delL"jG9[C6}NKnjupo8:3*@O,4G:!^5;agsOCD=nz w^T" G`@)ŀΕTgL{|np kݍu܋IAQmnc$+ Yׄ*'0#e =܀W"CnO:75 4潏 ȯڂؿztY>A;۴iqn)ztwPɘȇ2gζ>>v桚Y°<l&p]XZm!bDBYQTfibdȪK{كX gcCu1_E'I6-i*:1|}iS;-*M%0Rk1 d8 `"cŰZES6hU7чs-#-?f1[FIg?fm!'}}^Jyf]!6h},pmoKY{xzPԻiUz:HGPL#Nsy Q6}e=)Fub٢|ps±ΰߴU5Zu2ksIbgaʻӽVay.w/3V}Zn& 8g"QeHȶ @=D*οV>x&c(˘ըZ@6J[g2xI.W̖FZ qU~ ~r;rdN, \%KOpcKS6W#ie⟀Z*J d^>oSuE&D^6[Cuppa0_f%w⸲g`rEż)W$놚/߷&6 r1z] tKO#UQRq]HV5C~PzO 5֏.ХGZ9^@Z~/}N ^\AXe:/N=n(uv;`;}ìO}G4`%X}myjxc je95`2Yh 6Ip ч)LN˩nޜ(-n }7sEt1vvȗ͠%le/C5 w")b o‰B\R@ }A,*;oef}vYКIuD'HiY%d݀~6b}ĕ (r**z-^;t_mAE,P3_\lGiNв. 7s`y`(+`f D6XHrݍvcm%_rw G\8R,@BBKfM`XX ] HN 3۟{J>z/4pfJl v#ԊG9ⲵrguMvSM(7y˘v3:QJw`!G{|ӵ&$i{QHձTMTᛚqbd(vcS2gn7 u:[Fi¦U%ͬʺ CUˀ\7%u?2{dQܵ)%g6_ucov&ں~F V8VX,W)Eus xG!Q5ܳc2twi-ސlcwV17 =:tjOb?@ $/}_gbhKC:i8t]ؾ,Q>)o ģG;-G̓W >E=8C[)xwXSLJz, !_o"p+)AgrLV! ^foC7b2Xr6X?+!EIr^9W@"*O< Hk+`iǬ*J@*nﰹ^0~eG;"ԓ*>du9@}BXRm6vשaopiҘ+N;]g}֝'H'y!ZrL;["&8~m]*!Wb+/,>.D'MOƤ:JPS2C"dh}' RL7 $ź~ w^tIuWtHC1!J LoN!GK0Y]Z ԖY1{'u?.OàjK+zclsr:5wTWbnܹzk_m7=e,652y[1MߤL!`] .b昞oTe^ n2\H6GȄ|C՝/^( !"2o(a2QM? OĩG;ZGSf sofqDAMf]e*%GKQS.;;ػrr! @Q$ڻ_DlvjijVȳR`KL{%'m44܇O>9Ԡ竭1g?}-_( A{j/'@иpz^!{i3tq@7jX'6 .% a`<\7Ⴐa]$ vϬUq8_wO3}I!4tz68ѐu37q AE̓ET <@@@DܼфN6wGY>g}G;\uن{yW>+.W]V?LwP{˻7T gXumW#!Yjߪ , qjrv&bIr[!fm|oIJ'Օgl\,e&rSD|nO@kVp[k _ER ]7+kPjҪC3++݈GR cY[;8TNJ4+g-[#:.+gEfKF֘bƼM ʸCC\PRSӱ4F%]<"2D7asa5nT;w cR*_˓za hE .i[ޢ,Od7-*2KdA_ y ` *Pr% [v[DZ~͞ռ5ڎ Sx+KE*~'l50)'C"BiE1e5:Ƴ2?[T>ęcgxGd"e ̓-sQږ|>> pp>5e%>pf%nPңKhmj=hEoVWks#U\*3JwمWG]&7Dx.D\ai8QvpqN0cy. E_5q47C-Z2݄+E^HZr_V]hڮPj8 w F;rkOL3I1cb\rTc5sW: jwJ&M 3 47nRXѼq4lI:1n/fV9q>#+a?`[\+}dtIOAb+r2%6KQS)+C'/J~fd6Jh }Aۚ8[M(ήM,P'ށُ'iwj".F)u]+ p<8ٗDB,^Ycw_z1CKV嫰1xi^A:F#rUtbICz94)k Q˔5RpI7u52}8e%~S{g?p ĴnC` ĭڲPqQH ~aϲ}R4ˆEwQTίbOӥ롼PPBJe݉}u]1+ĿSöd(_$CtGӡZlpr*:CBbFS ]8 u ^4H[nJ}ԗvl@тT̙?x5i4^]ạ kZʢ\`_w% t;*pZ_cX6d{a~bg2ѹOpކItTYUFO "Nnj 9G$5bJc["?23%7HX!; iPC JљC$ާe8k$vs"Gtvn-e'?ϬJ)y[|^Nj6PwJa)`ۂ [#ZΛw953-cS4O_2\`Gb!h.O,TXyU~`~xz`>kӓ3r+޿u2R4^Gg%\POG? 4"x24ɹ7eZ,'OkM"+)|X: ˹(M3۞yX+L0!d UXtL$* )692ӔFCsuJ=fL6:ɮ0)ۚ%Qb=*ډ;Z:nEO`YpZsAA0Xx녮K=).~sP +T{l.~F!laZ#2(UtxIXO3Z] t''df~)"Nt#7c,:`gR!3=Û乶SX⊘YV65hD\Hց4O5|tt$_1L{ˈ|V0+',FJWD>*xwıv[ 7Wgw5I`N9]dQS=fvz ||wꋼLw#2 ) kQa#Q,w+ -ٚ!}pg9wwVE*^#NU9hŔHP޳vq%W öz p~m?OŞMP/%ήMB+af ;,-7`wnZ}ka f 0HfwKcC>BEV*ޞKC-bG +;n*]+\b;T kFkӫtnyF%o$6ܔ|/זV]PIk0b8uq+3لZ魗<_Ie». x}xNDe J4Vgڽ\\ۤyd[f;WTvW\KgP:$yKƖO dʫ qkOJLE $ h齝46}(ٻG/"6!v>Ը՘"6H]na .~6) y®6rPE<$XIض`H.ߜ}c~% ۚ05{wh#S x{BxUAl\FH7[ʐI2O'X;JQu S\I-c$!)ؔ4VHc{"1p,2!~nd4c(MdgzЕdobsoXneIzdLPձi.6b`MťoWqX+>K4Ve3o5 bSyp}=[ߝw5'>nkp6>C40_b[W\zCȗ7%ز9W8dP8ۗh3Vdw&Yj³ez:hҦLkP ~PYY-s<5e墵[K2bE# ?%ǜQtHRμe|lgufhaٴV%:`|誷(;MRM TU<f4.i'zT'Ȁsy %fG@'anU)T^9!@'[q i:*2l/Egh"FߣN)xdIPՑ2x%0uE0Kx|0 tvc]8[G fn?_[]KQ׺HTлӺlq'Df4qi,&[ֶ\ɥHa۵]BH?p bc <CEޗLjE}۵td;L0 9]oG \e(4W;n[^ʸEF?.JPh?z7;O+?\g'nI*xw^c$WKcx=~ka巚 4WcAȑ>񘅔aŧ ASw.z{D&JQr'ls5B]nuHC^t۷d_w8&*]!v!nJк{i% SYU˅.% [6Cn YhsFM({?;S#,Yأ~ ƩYۜ(` w-Z:`<=6_1V?,bn$øM](p3 (Yyj=9X"F9ƪ3Sc+ 0E\` }UӘWS*"L'=\TĐܝLGpk0Oz/f!: &B_ ^yivUSA;"ʈZt U.9`Iun;DSVhQ5%^-t`,M&r|:.O\R%xMQDʕi`a_DNYdb=۳@Z( /#",;V>;A!cj׌\|W-)'\sQ:z:>(6,=Fh3;|rD j ;GdaN k:ٞ^ E"އє)!gM9!'IGлIqcNb/]2CIɤN7d[BDz#[[Xx/Y80u p2_H&IaĹ"(6=ͿNb%K_µ~{ݢX726;vN yᆉūeD3?"[ȴ4 V9[yaWTj?Nkw2 ޅ.K c"ƽZ F7!b< Sly B|DPμt}0 XTvw:"!;3E|w$ݩ9;NEϳnvRE hxljeNʽ4BnN쳺[Sӟ+Kg(5S ʜ'gմCt0_9z|%WI{0&ݜ{̔9q w2y#$1nɖ*Z<ػgxiɽc,ShVE%1~cX5 <\qb\wP!.N2%lE`3}ύs҇G  Bqww>0+?_&-^~PtҹOTU樉h>rw%J$>ÈAМZo|r$q27iKY>ѯP}r 1qx\M`ˠoL(mN)pM ^Ze]4-2+Z|$  qxƻ*]NFF3Ru:hCƓFAd116E8Rfty ֟c2iqcx ˬ?qQ5 HP]q>͟ !pjTfC-neσX>%%5`=ܦweF&_ zBT3o߄rSf5!,}v~H:G)KSy-+L@vݰL|.: %ϽwТ^5|Wax{v/~X:QTIňUj#RAb!,f=G ~&rvfO*F*‡0tulf^ml׆- Gg-yc77HiuDqۤ٘oDv%?FPݭJpI|1ȿէ/UƥOJ1 W.y7 %Vp縷h6ac:k~ #E@ўK{ ,MAF":pGdF #E x1Xx58wZ;*)9ֳH@{0K&kx~e Q  U92"7"Nlܚ%e Y*a{ <[V1dj-4u}zL.ӃKF57{kt{DцO3%!2\ )6gDsV6*BN1(H 4Zq1\uEdZdlΦ7T#i~9CTd9irL!ܺЏ 1ޭg]ZnKrvxQzB4+, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2026-02-04 16:51+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. type: Title = #: ceccomp.adoc:1 #, no-wrap msgid "ceccomp(1)" msgstr "" #. type: Plain text #: ceccomp.adoc:3 msgid "dbgbgtf ; RocketDev " msgstr "" #. type: Title == #: ceccomp.adoc:11 #, no-wrap msgid "NAME" msgstr "" #. type: Plain text #: ceccomp.adoc:14 msgid "ceccomp - A tool to analyze seccomp filters" msgstr "" #. type: Title == #: ceccomp.adoc:15 #, no-wrap msgid "SYNOPSIS" msgstr "" #. type: Plain text #: ceccomp.adoc:20 #, no-wrap msgid "" " usage: ceccomp [FILE] " "[-q|--quiet]\n" " [-f|--format FMT] [-a|--arch ARCH] [-p|--pid PID] " "[-s|--seize]\n" " [-o|--output FILE] [-c|--color WHEN] ...\n" msgstr "" #. type: Title == #: ceccomp.adoc:21 #, no-wrap msgid "CONCEPT" msgstr "" #. type: Plain text #: ceccomp.adoc:26 msgid "" "Kernel use BPF filters to limit syscall rules, applied via `seccomp` or " "`prctl` syscall. For example, down below is a simple filter to block " "`execve` syscall in hex format:" msgstr "" #. type: Plain text #: ceccomp.adoc:31 #, no-wrap msgid "" " 1: 20 00 00 00 00 00 00 00 $A = $syscall_nr\n" " 2: 15 00 00 01 3b 00 00 00 if ($A != execve) goto 4\n" " 3: 06 00 00 00 00 00 00 00 return KILL\n" " 4: 06 00 00 00 00 00 ff 7f return ALLOW\n" msgstr "" #. type: Plain text #: ceccomp.adoc:35 msgid "" "The part presented in hex is what kernel received, and `ceccomp` take it to " "disassemble back to human readable text. For instance the *lineno* in the " "left and *statement* in the right." msgstr "" #. type: Plain text #: ceccomp.adoc:38 msgid "" "Later I'll use _TEXT_ in short for BPF human readable text, and use _RAW_ in " "short for BPF raw format, please keep that in mind." msgstr "" #. type: Title == #: ceccomp.adoc:39 #, no-wrap msgid "DESCRIPTION" msgstr "" #. type: Plain text #: ceccomp.adoc:44 msgid "" "`ceccomp` have 5 main functions, basically it's a C version of " "`seccomp-tools`, however, there are some breaking changes you need to know, " "which will be highlighted in each subcommand section." msgstr "" #. type: Title === #: ceccomp.adoc:45 #, no-wrap msgid "asm - ASSEMBLE" msgstr "" #. type: Plain text #: ceccomp.adoc:48 #, no-wrap msgid " ceccomp asm [-c WHEN] [-a ARCH] [-f FMT] [TEXT]\n" msgstr "" #. type: Plain text #: ceccomp.adoc:51 msgid "" "Assemble _TEXT_ to _RAW_. Use it to embed hand written filter rules into C " "code or to see the original code of some _TEXT_." msgstr "" #. type: Labeled list #: ceccomp.adoc:52 ceccomp.adoc:101 ceccomp.adoc:144 ceccomp.adoc:194 #, no-wrap msgid "WHEN" msgstr "" #. type: Plain text #: ceccomp.adoc:56 msgid "" "Determines when to display warnings and errors in color. If the value is " "_auto_, ceccomp will display color when the output target is a \"tty\". Can " "be _auto_, _never_ or _always_. The default value is _auto_." msgstr "" #. type: Labeled list #: ceccomp.adoc:57 ceccomp.adoc:105 ceccomp.adoc:159 #, no-wrap msgid "ARCH" msgstr "" #. type: Plain text #: ceccomp.adoc:62 msgid "" "Set to any architecture libseccomp supports. Will be used to determine the " "actual syscall number behind the name (for example, on x86_64, you could " "write `\"execve\"` instead of `59` like the basic example above). Your " "system arch will be taken if not set via `uname`. The default value on your " "system is {ARCH}." msgstr "" #. type: Plain text #: ceccomp.adoc:66 msgid "" "Since _version 4.0_, endianness is considered. If target endianness *ARCH* " "is different from machine endianness, the filters will be reversed (CODE and " "K) before outputting." msgstr "" #. type: Labeled list #: ceccomp.adoc:67 #, no-wrap msgid "FMT" msgstr "" #. type: Plain text #: ceccomp.adoc:71 msgid "" "Determines how `ceccomp` produces binary-format bpf code. Can be _hexfmt_, " "_hexline_ or _raw_. You could find sample output in <> section. " "The default value is _hexline_." msgstr "" #. type: Labeled list #: ceccomp.adoc:72 ceccomp.adoc:162 #, no-wrap msgid "TEXT" msgstr "" #. type: Plain text #: ceccomp.adoc:75 msgid "" "Take a optional filename to determine which file containing _TEXT_ will be " "assembled. Will read from _stdin_ if not set. `-` is treated as _stdin_." msgstr "" #. type: Plain text #: ceccomp.adoc:78 msgid "" "The assembly syntax was changed greatly since _version 4.0_, please checkout " "grammar reference below!" msgstr "" #. type: Plain text #: ceccomp.adoc:81 msgid "" "Please check out <> section to see how to write a " "rule by hand. Some examples will be displayed in <> section." msgstr "" #. type: Table #: ceccomp.adoc:92 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools asm`\n" "|Use its own grammar to assemble, a bit script like; can assemble invalid " "_TEXT_\n" "which will be rejected by kernel\n" "\n" "|`ceccomp asm`\n" "|You can just take `disasm` output to `asm`, no new grammar is needed to " "learn;\n" "take `stdin` as input by default\n" msgstr "" #. type: Title === #: ceccomp.adoc:94 #, no-wrap msgid "disasm - DISASSEMBLE" msgstr "" #. type: Plain text #: ceccomp.adoc:97 #, no-wrap msgid " ceccomp disasm [-c WHEN] [-a ARCH] [RAW]\n" msgstr "" #. type: Plain text #: ceccomp.adoc:100 msgid "" "Disassemble _RAW_ to _TEXT_. Use it to see what does a filter do if you " "could not access filter via `trace` and have to manually extract the filter " "out." msgstr "" #. type: Plain text #: ceccomp.adoc:104 msgid "" "Argument description can be found in <> section. `disasm` " "may print more text in color including syntax highlighting for _TEXT_." msgstr "" #. type: Plain text #: ceccomp.adoc:110 msgid "" "Set to any architecture libseccomp supports. Will be used to determine how " "filtered syscall number in _RAW_ filter is translated to syscall name (for " "example, on x86_64, the number `0x3b` is translated to `execve` if is " "comparing syscall_nr, see the basic example above). The default value on " "your system is {ARCH}." msgstr "" #. type: Labeled list #: ceccomp.adoc:111 #, no-wrap msgid "RAW" msgstr "" #. type: Plain text #: ceccomp.adoc:114 msgid "" "A binary file with raw BPF codes. Takes _stdin_ as input if not set. Treat " "`-` as _stdin_. The file is arch-revelent, so it may not be portable on " "different archs." msgstr "" #. type: Plain text #: ceccomp.adoc:118 msgid "" "Since _version 4.0_, endianness is considered. If target endianness *ARCH* " "is different from machine endianness, the filters will be reversed (CODE and " "K) before decoding." msgstr "" #. type: Plain text #: ceccomp.adoc:123 msgid "" "ceccomp will try to resolve syscall number under an arch ONLY IF that at " "that line, arch can be determined. On foreign arch (not equal to the arch " "you set), the foreign arch will be prepended to syscall name. You may notice " "that in some cases, seccomp-tools is able to resolve the name while ceccomp " "is not, that may be intended as the arch is not determined." msgstr "" #. type: Table #: ceccomp.adoc:133 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools disasm`\n" "|Disassembles in its format; never check if the filter is valid\n" "\n" "|`ceccomp disasm`\n" "|Disassembles in ceccomp format, and takes `stdin` as input by default; " "check arch strictly\n" "and always display foreign arch name\n" msgstr "" #. type: Title === #: ceccomp.adoc:135 #, no-wrap msgid "emu - EMULATE" msgstr "" #. type: Plain text #: ceccomp.adoc:138 #, no-wrap msgid "" " ceccomp emu [-c WHEN] [-a ARCH] [-q] TEXT SYSCALL_NAME/SYSCALL_NR " "[ARGS[0] ARGS[1] ... ARGS[5] PC]\n" msgstr "" #. type: Plain text #: ceccomp.adoc:143 msgid "" "Emulate what will happen if `syscall(SYSCALL_NR, ARGS[0], ARGS[1], ..., " "ARGS[5])` from `PC` is called following rules described in _TEXT_. Use it to " "see the result without actually running it in program or you don't want to " "examine the filter rule manually. This subcommand can be used to " "automatically examining a filter." msgstr "" #. type: Plain text #: ceccomp.adoc:147 msgid "" "Argument description can be found in <> section. `emu` may " "print more text in color including syntax highlighting for _TEXT_ and " "skipped statements." msgstr "" #. type: Labeled list #: ceccomp.adoc:148 #, no-wrap msgid "SYSCALL_NAME/SYSCALL_NR" msgstr "" #. type: Plain text #: ceccomp.adoc:153 msgid "" "If you set *SYSCALL_NAME* (like `execve`), it will be translated to " "*SYSCALL_NR* under *ARCH* first. Or else set *SYSCALL_NR* directly (like " "`59`). Then the nr will be tested against the bpf filter to see the result " "of that syscall. This argument is NOT optional." msgstr "" #. type: Labeled list #: ceccomp.adoc:154 #, no-wrap msgid "ARGS[0-5] and PC" msgstr "" #. type: Plain text #: ceccomp.adoc:158 msgid "" "Register values when calling syscall. For example, on x86_64, these are " "equivalent to `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9` and `rip`. Their " "default value is 0." msgstr "" #. type: Plain text #: ceccomp.adoc:161 msgid "Argument description can be found in <> section." msgstr "" #. type: Plain text #: ceccomp.adoc:166 msgid "" "Take a filename to determine which file containing _TEXT_ rule will be " "tested. Note that filename CAN NOT be ignored as ceccomp can not determine " "if a positional argument is syscall or filename. Use `-` to refer to " "_stdin_." msgstr "" #. type: Labeled list #: ceccomp.adoc:167 ceccomp.adoc:219 #, no-wrap msgid "-q, --quiet" msgstr "" #. type: Plain text #: ceccomp.adoc:170 msgid "" "Only print the eval result of the filter. For example, if last statement " "emulated is `return KILL`, then `KILL` is printed." msgstr "" #. type: Table #: ceccomp.adoc:180 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools emu`\n" "|Take a _RAW_ as input\n" "\n" "|`ceccomp emu`\n" "|Take a _TEXT_ as input and take `stdin` as input by default; set *PC* is\n" "possible\n" msgstr "" #. type: Title === #: ceccomp.adoc:182 #, no-wrap msgid "trace - TRACE FILTER IN RUNTIME" msgstr "" #. type: Plain text #: ceccomp.adoc:186 #, no-wrap msgid "" " ceccomp trace [-c WHEN] [-q] [-o FILE] PROGRAM [program-args]\n" " [-c WHEN] [-q] -p PID [-s]\n" msgstr "" #. type: Plain text #: ceccomp.adoc:193 msgid "" "The first line captures filters *PROGRAM* loads in runtime by tracing it; " "the second line extract seccomp filters from *PID*, or trace *PID* to " "capture subsequent seccomp filters; once fetched filters, print them in " "_TEXT_. You can only choose one of the two formats above. Use this if " "running the program is the simplest way to fetch bpf filters or a program " "with seccomp filters installed is waiting for input." msgstr "" #. type: Plain text #: ceccomp.adoc:197 msgid "" "Argument description can be found in <> section. `trace` may " "print more text in color including syntax highlighting for _TEXT_." msgstr "" #. type: Labeled list #: ceccomp.adoc:198 #, no-wrap msgid "FILE" msgstr "" #. type: Plain text #: ceccomp.adoc:203 msgid "" "May be useful when *PROGRAM* produces quite a lot output in _stderr_. " "`ceccomp` allow user to close _stdin_ and _stdout_ to limit *PROGRAM* input " "and output, so `ceccomp` use _stderr_ to print messages when running " "*PROGRAM*, set *FILE* if you want to see _TEXT_ in some other file. Treat " "`-` as _stdout_." msgstr "" #. type: Labeled list #: ceccomp.adoc:204 #, no-wrap msgid "PROGRAM" msgstr "" #. type: Plain text #: ceccomp.adoc:207 msgid "" "Set to the program you want to run, and *program-args* are its arguments " "just like running shell command `exec PROGRAM program-args`." msgstr "" #. type: Labeled list #: ceccomp.adoc:208 #, no-wrap msgid "PID" msgstr "" #. type: Plain text #: ceccomp.adoc:213 msgid "" "Set to the pid you want to inspect. *PID* is conflict with *PROGRAM*; you " "could either run a program dynamically or examine a pid in one command. " "Without `-s` flag, trace pid will try to extract seccomp filter in *PID* via " "`ptrace(PTRACE_SECCOMP_GET_FILTER)`, which may not be available in some " "systems." msgstr "" #. type: Labeled list #: ceccomp.adoc:214 #, no-wrap msgid "-s, --seize" msgstr "" #. type: Plain text #: ceccomp.adoc:218 msgid "" "*ONLY AVAILABLE FOR TRACE PID MODE.* Set this flag will override trace pid " "behavior to attach to *PID* and keep tracing for seccomp filter loading like " "trace prog mode. _This flag was introduced in version 4.0._" msgstr "" #. type: Plain text #: ceccomp.adoc:222 msgid "" "Set to suppress the extra *[INFO]* prints when detect process forking, " "exiting or seccomp filter loading. _This flag was introduced in version " "4.0._" msgstr "" #. type: Plain text #: ceccomp.adoc:226 msgid "" "To extract filters from *PID*, `CAP_SYS_ADMIN` is needed (without `-s` flag) " "and `CAP_SYS_PTRACE` may also be needed, the easiest way to acquire them is " "calling `ceccomp` with `sudo`." msgstr "" #. type: Plain text #: ceccomp.adoc:230 msgid "" "Since _version 3.1_, multiple process tracing is introduced, and when tracee " "forking/resolving/exiting, an extra INFO message is printed. You can discard " "it by running command like `ceccomp trace -q PROG 2>/dev/null`." msgstr "" #. type: Table #: ceccomp.adoc:243 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools dump`\n" "|Setting output format is possible; each filter can be output to a " "different\n" "file; killing *PROGRAM* once *LIMIT* times of filters loaded; wrapping " "*PROGRAM*\n" "in `sh -c`\n" "\n" "|`ceccomp trace`\n" "|All filters are output to a single file; never kill *PROGRAM*; *PROGRAM* " "is\n" "launched directly, so `./` is not needed; explicitly print when forking;\n" "able to attach to pid for dynamic seccomp filter capturing\n" msgstr "" #. type: Title === #: ceccomp.adoc:245 #, no-wrap msgid "probe - TEST COMMON SYSCALLS INSTANTLY" msgstr "" #. type: Plain text #: ceccomp.adoc:248 #, no-wrap msgid " ceccomp probe [-c WHEN] [-o FILE] [-q] PROGRAM [program-args]\n" msgstr "" #. type: Plain text #: ceccomp.adoc:252 msgid "" "Run *PROGRAM* with *program-args* to captures *FIRST* seccomp filter, and " "then kill all children. Use it when a quick check against a program is " "needed, and detect potential seccomp rule issues." msgstr "" #. type: Plain text #: ceccomp.adoc:254 msgid "" "All argument descriptions can be found in <> section." msgstr "" #. type: Plain text #: ceccomp.adoc:258 msgid "" "The output for this subcommand is the emulating result of common syscalls " "like `execve`, `open` and so on. If the filter itself is not capable of " "blocking syscalls, you could know that with a glance." msgstr "" #. type: Plain text #: ceccomp.adoc:261 msgid "" "Typical output for this subcommand is described below, more detailed example " "could be found in <> section." msgstr "" #. type: Plain text #: ceccomp.adoc:273 #, no-wrap msgid "" " open -> ALLOW\n" " read -> ALLOW\n" " write -> ALLOW\n" " execve -> KILL\n" " execveat -> KILL\n" " mmap -> ALLOW\n" " mprotect -> ALLOW\n" " openat -> ALLOW\n" " sendfile -> ALLOW\n" " ptrace -> ERRNO(1)\n" " fork -> ALLOW\n" msgstr "" #. type: Plain text #: ceccomp.adoc:275 msgid "`seccomp-tools` don't have this subcommand." msgstr "" #. type: Title == #: ceccomp.adoc:276 #, no-wrap msgid "TEXT GRAMMAR REFERENCE" msgstr "" #. type: Plain text #: ceccomp.adoc:283 msgid "" "The grammar changed greatly since _version 4.0_ as we refactored lexer for " "better human readability. The wrapper now prefixed by `#` as it's a comment " "now. And _line_ is replaced by _label_, so now lexer depends on label " "declaration to decide where to jump, instead of lineno in _TEXT_ file." msgstr "" #. type: Plain text #: ceccomp.adoc:288 msgid "" "A valid _TEXT_ format is described in EBNF-like declaration here: " "https://github.com/dbgbgtf1/Ceccomp/issues/17#issuecomment-3610531705. If " "you have no interest to know what EBNF is, please keep reading for examples." msgstr "" #. type: Plain text #: ceccomp.adoc:290 msgid "BPF ops which are not described below are banned by kernel." msgstr "" #. type: Title === #: ceccomp.adoc:291 #, no-wrap msgid "Comment and Label" msgstr "" #. type: Plain text #: ceccomp.adoc:295 msgid "" "`ceccomp disasm` displays a lot of things, but some of them are optional for " "asm." msgstr "" #. type: Plain text #: ceccomp.adoc:300 #, no-wrap msgid "" " #Label CODE JT JF K\n" " #---------------------------------\n" " L0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW\n" " #---------------------------------\n" msgstr "" #. type: Plain text #: ceccomp.adoc:302 msgid "Any text after `#` will be discarded by asm like some script languages." msgstr "" #. type: Plain text #: ceccomp.adoc:304 msgid "Empty lines are accepted." msgstr "" #. type: Plain text #: ceccomp.adoc:312 msgid "" "Label declaration is an identifier at the beginning of line and suffixed by " "`:` like `L0001`. An identifier is a string starts with alpha and contains " "with only alphanumeric characters and underscore `_`. Label is only " "necessary if it's the destination of `goto`, these redundant labels added by " "disasm are for readability. E.g. in `if ($A == 0) goto somewhere`, " "`somewhere` is a label and must be declared after the statement. Label " "declaration can take a line separately, or be put in front of statement." msgstr "" #. type: Plain text #: ceccomp.adoc:315 msgid "" "The `CODE`, `JT`, `JF` and `K` value generated by disasm will be discarded " "by asm, asm only parse the effective statement after `K`." msgstr "" #. type: Plain text #: ceccomp.adoc:320 msgid "" "There are some slight difference between `ceccomp disasm` and `seccomp-tools " "disasm`, down below is a general example. And some statements are different, " "so don't pipe seccomp-tools output to ceccomp blindly." msgstr "" #. type: Plain text #: ceccomp.adoc:324 #, no-wrap msgid "" " line CODE JT JF K\n" " =================================\n" " 0000: 0x06 0x00 0x00 0x7fff0000 return ALLOW\n" msgstr "" #. type: Title === #: ceccomp.adoc:325 #, no-wrap msgid "Assignment" msgstr "" #. type: Plain text #: ceccomp.adoc:329 msgid "" "`A` can be set to seccomp attributes directly. But `X` can not be assigned " "with seccomp attributes directly due to kernel limit." msgstr "" #. type: Plain text #: ceccomp.adoc:332 #, no-wrap msgid "" " $A = $arch\n" " $A = $syscall_nr\n" msgstr "" #. type: Plain text #: ceccomp.adoc:334 msgid "" "To assign `A` with those 64-bit long fields, `low_` or `high_` prefix is " "needed." msgstr "" #. type: Plain text #: ceccomp.adoc:342 #, no-wrap msgid "" " $A = $low_pc\n" " $A = $high_pc\n" " $A = $low_args[0]\n" " $A = $high_args[0]\n" " ...\n" " $A = $low_args[5]\n" " $A = $high_args[5]\n" msgstr "" #. type: Plain text #: ceccomp.adoc:345 msgid "" "A special attribute is `sizeof(struct seccomp_data)`, that can be assigned " "to `A` or `X` directly." msgstr "" #. type: Plain text #: ceccomp.adoc:348 #, no-wrap msgid "" " $A = $scmp_data_len\n" " $X = $scmp_data_len\n" msgstr "" #. type: Plain text #: ceccomp.adoc:352 msgid "" "Temporary memory is 32-bit, to access them, you could use hex or dec as " "index. Both `A` and `X` is assignable. Assigning immediate values to `A` or " "`X` accepts any format of number if you imply the correct base by \"0x\" or " "\"0b\"." msgstr "" #. type: Plain text #: ceccomp.adoc:360 #, no-wrap msgid "" " $X = $mem[0]\n" " $A = $mem[0xf]\n" " $A = $mem[15] # both hex and dec index are OK\n" " $A = 0\n" " $X = 0x3b\n" " $A = 0b1111\n" " $A = 0333\n" msgstr "" #. type: Plain text #: ceccomp.adoc:363 msgid "" "You could also assign `X` to `A` or in the reverse order. Assign `X` or `A` " "to temporary memory is definitely okay." msgstr "" #. type: Plain text #: ceccomp.adoc:368 #, no-wrap msgid "" " $A = $X\n" " $X = $A\n" " $mem[3] = $X\n" " $mem[0x4] = $A\n" msgstr "" #. type: Title === #: ceccomp.adoc:369 #, no-wrap msgid "Arithmetic Operations" msgstr "" #. type: Plain text #: ceccomp.adoc:372 msgid "Various operations can be applied to `A`." msgstr "" #. type: Plain text #: ceccomp.adoc:379 #, no-wrap msgid "" " $A += 30\n" " $A -= 4\n" " $A *= 9\n" " $A /= 1\n" " $A &= 7\n" " $A >>= 6\n" msgstr "" #. type: Plain text #: ceccomp.adoc:381 msgid "The right value can be `X`." msgstr "" #. type: Plain text #: ceccomp.adoc:386 #, no-wrap msgid "" " $A &= $X\n" " $A |= $X\n" " $A ^= $X\n" " $A <<= $X\n" msgstr "" #. type: Plain text #: ceccomp.adoc:388 msgid "And there is a way to negativate `A`." msgstr "" #. type: Plain text #: ceccomp.adoc:390 #, no-wrap msgid " $A = -$A\n" msgstr "" #. type: Title === #: ceccomp.adoc:391 #, no-wrap msgid "Jump Downwards If ..." msgstr "" #. type: Plain text #: ceccomp.adoc:394 msgid "Unconditional jump:" msgstr "" #. type: Plain text #: ceccomp.adoc:396 #, no-wrap msgid " goto L3\n" msgstr "" #. type: Plain text #: ceccomp.adoc:398 msgid "Jump if:" msgstr "" #. type: Plain text #: ceccomp.adoc:404 #, no-wrap msgid "" " if ($A == execve) goto L3\n" " if ($A != 1234) goto L4\n" " if ($A & $X) goto L5\n" " if !($A & 7) goto L6\n" " if ($A <= $X) goto L7\n" msgstr "" #. type: Plain text #: ceccomp.adoc:406 msgid "If true jump to ... if false jump to...:" msgstr "" #. type: Plain text #: ceccomp.adoc:409 #, no-wrap msgid "" " if ($A > $X) goto L3, else goto L4\n" " if ($A >= 4567) goto L5, else goto L6\n" msgstr "" #. type: Plain text #: ceccomp.adoc:416 msgid "" "ONLY in conditions, you CAN replace number with syscall name or arch name. " "In example above, `0x3b` is replaced by `execve`. All the syscall name will " "be resolved to syscall number under your selected arch. If you want to " "resolve a syscall name in foreign arch (not equal to your selected arch), " "please prepend a arch and dot. For example, your arch is x86_64, and you are " "writing _aarch64_ rules, then please write like:" msgstr "" #. type: Plain text #: ceccomp.adoc:418 #, no-wrap msgid " if ($A == aarch64.read) goto 5\n" msgstr "" #. type: Plain text #: ceccomp.adoc:421 msgid "" "Note that if you manually set arch to _aarch64_ with `-a aarch64`, you can " "omit `aarch64.` in statement." msgstr "" #. type: Title === #: ceccomp.adoc:422 #, no-wrap msgid "Return Code" msgstr "" #. type: Plain text #: ceccomp.adoc:425 msgid "Return value of register `A`:" msgstr "" #. type: Plain text #: ceccomp.adoc:427 #, no-wrap msgid " return $A\n" msgstr "" #. type: Plain text #: ceccomp.adoc:431 msgid "" "Or return a immediate value, with extra field in `()`. Actions including " "`TRACE`, `TRAP` and `ERRNO` accept an extra field, without `()`, they are " "treated as `action(0)`." msgstr "" #. type: Plain text #: ceccomp.adoc:441 #, no-wrap msgid "" " return 0x13371337\n" " return KILL\n" " return KILL_PROCESS\n" " return TRAP(123)\n" " return ERRNO(0)\n" " return TRACE\n" " return TRACE(3)\n" " return LOG\n" " return NOTIFY\n" msgstr "" #. type: Title === #: ceccomp.adoc:442 #, no-wrap msgid "Short Example" msgstr "" #. type: Plain text #: ceccomp.adoc:446 msgid "" "The following _TEXT_ is valid for asm, which blocks `execve` and `execveat` " "for amd64 syscalls:" msgstr "" #. type: Plain text #: ceccomp.adoc:452 #, no-wrap msgid "" " $A = $syscall_nr\n" " if ($A == execve) goto forbid\n" " if ($A == execveat) goto forbid\n" " return ALLOW\n" " forbid: return KILL\n" msgstr "" #. type: Title == #: ceccomp.adoc:453 #, no-wrap msgid "RESTRICTIONS" msgstr "" #. type: Plain text #: ceccomp.adoc:456 msgid "Ceccomp asm put some restrictions on _TEXT_ for better performance." msgstr "" #. type: Plain text #: ceccomp.adoc:458 msgid "`'\\0'` must not be found in _TEXT_ since it's a text file." msgstr "" #. type: Plain text #: ceccomp.adoc:459 msgid "A line must be shorter than 384 *bytes*." msgstr "" #. type: Plain text #: ceccomp.adoc:460 msgid "A _TEXT_ file must have less than 4096 lines." msgstr "" #. type: Plain text #: ceccomp.adoc:461 msgid "A _TEXT_ file must be smaller than 1 MiB." msgstr "" #. type: Plain text #: ceccomp.adoc:464 msgid "" "And for both asm and disasm, effective statements (that can be encoded or " "decoded into BPF) must be less or equal than 1024, this is enforced by " "kernel." msgstr "" #. type: Plain text #: ceccomp.adoc:467 msgid "" "A fun fact about ceccomp asm: any basic ANSI color in _TEXT_ file, e.g., " "`\\x1b[31m`, will be discarded when processing." msgstr "" #. type: Title == #: ceccomp.adoc:468 #, no-wrap msgid "EXAMPLES" msgstr "" #. type: Plain text #: ceccomp.adoc:473 msgid "" "Manpage can not display images, so please check out html version of this " "page to see examples." msgstr "" #. type: Title === #: ceccomp.adoc:476 #, no-wrap msgid "asm example" msgstr "" #. type: Target for macro image #: ceccomp.adoc:477 #, no-wrap msgid "asm.webp" msgstr "" #. type: Title === #: ceccomp.adoc:478 #, no-wrap msgid "disasm example" msgstr "" #. type: Target for macro image #: ceccomp.adoc:479 #, no-wrap msgid "disasm.webp" msgstr "" #. type: Title === #: ceccomp.adoc:480 #, no-wrap msgid "emu example" msgstr "" #. type: Target for macro image #: ceccomp.adoc:481 #, no-wrap msgid "emu.webp" msgstr "" #. type: Title === #: ceccomp.adoc:482 #, no-wrap msgid "trace example" msgstr "" #. type: Plain text #: ceccomp.adoc:484 msgid "Running program:" msgstr "" #. type: Target for macro image #: ceccomp.adoc:485 #, no-wrap msgid "trace.webp" msgstr "" #. type: Plain text #: ceccomp.adoc:488 msgid "If set `-o FILE`:" msgstr "" #. type: Target for macro image #: ceccomp.adoc:489 #, no-wrap msgid "output_trick.webp" msgstr "" #. type: Plain text #: ceccomp.adoc:492 msgid "Trace pid mode:" msgstr "" #. type: Target for macro image #: ceccomp.adoc:493 #, no-wrap msgid "trace_pid.webp" msgstr "" #. type: Plain text #: ceccomp.adoc:496 msgid "Seize pid mode:" msgstr "" #. type: Target for macro image #: ceccomp.adoc:497 #, no-wrap msgid "trace_seize.webp" msgstr "" #. type: Plain text #: ceccomp.adoc:500 msgid "Completion for pid mode is available under zsh:" msgstr "" #. type: Target for macro image #: ceccomp.adoc:501 #, no-wrap msgid "trace_completion.webp" msgstr "" #. type: Title === #: ceccomp.adoc:503 #, no-wrap msgid "probe example" msgstr "" #. type: Target for macro image #: ceccomp.adoc:504 #, no-wrap msgid "probe.webp" msgstr "" #. type: Title == #: ceccomp.adoc:507 #, no-wrap msgid "REPO" msgstr "" #. type: Plain text #: ceccomp.adoc:511 msgid "" "Visit https://github.com/dbgbgtf1/Ceccomp to find the code. Pull Requests " "and Issues are welcome!" msgstr "" #. type: Plain text #: ceccomp.adoc:512 msgid "Copyright (C) 2025-present, distributed under GPLv3 or later." msgstr "" ceccomp-4.0/docs/po/zh_CN.po000066400000000000000000001277401514205130000156770ustar00rootroot00000000000000# Chinese translations for PACKAGE package # Copyright (C) 2026 Free Software Foundation, Inc. # This file is distributed under the same license as the PACKAGE package. # Automatically generated, 2026. # msgid "" msgstr "" "Project-Id-Version: ceccomp docs\n" "POT-Creation-Date: 2026-02-04 16:51+0800\n" "PO-Revision-Date: 2026-02-04 16:51+0800\n" "Last-Translator: Automatically generated\n" "Language-Team: RocketDev\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.8\n" #. type: Title = #: ceccomp.adoc:1 #, no-wrap msgid "ceccomp(1)" msgstr "ceccomp(1)" #. type: Plain text #: ceccomp.adoc:3 msgid "dbgbgtf ; RocketDev " msgstr "" "dbgbgtf ; RocketDev " #. type: Title == #: ceccomp.adoc:11 #, no-wrap msgid "NAME" msgstr "名称" #. type: Plain text #: ceccomp.adoc:14 msgid "ceccomp - A tool to analyze seccomp filters" msgstr "ceccomp - 一个分析安全计算 (seccomp) 过滤器的工具" #. type: Title == #: ceccomp.adoc:15 #, no-wrap msgid "SYNOPSIS" msgstr "大纲" #. type: Plain text #: ceccomp.adoc:20 #, no-wrap msgid "" " usage: ceccomp [FILE] [-q|--quiet]\n" " [-f|--format FMT] [-a|--arch ARCH] [-p|--pid PID] [-s|--seize]\n" " [-o|--output FILE] [-c|--color WHEN] ...\n" msgstr "" " usage: ceccomp [FILE] [-q|--quiet]\n" " [-f|--format FMT] [-a|--arch ARCH] [-p|--pid PID] [-s|--seize]\n" " [-o|--output FILE] [-c|--color WHEN] ...\n" #. type: Title == #: ceccomp.adoc:21 #, no-wrap msgid "CONCEPT" msgstr "概念" #. type: Plain text #: ceccomp.adoc:26 msgid "" "Kernel use BPF filters to limit syscall rules, applied via `seccomp` or " "`prctl` syscall. For example, down below is a simple filter to block " "`execve` syscall in hex format:" msgstr "" "内核使用BPF过滤器来限制系统调用规则,并使用 `seccomp` 和 `prctl` 两个系统调用" "来安装过滤器。以下是一个以十六进制表示的限制 `execve` 系统调用的简单过滤器:" #. type: Plain text #: ceccomp.adoc:31 #, no-wrap msgid "" " 1: 20 00 00 00 00 00 00 00 $A = $syscall_nr\n" " 2: 15 00 00 01 3b 00 00 00 if ($A != execve) goto 4\n" " 3: 06 00 00 00 00 00 00 00 return KILL\n" " 4: 06 00 00 00 00 00 ff 7f return ALLOW\n" msgstr "" " 1: 20 00 00 00 00 00 00 00 $A = $syscall_nr\n" " 2: 15 00 00 01 3b 00 00 00 if ($A != execve) goto 4\n" " 3: 06 00 00 00 00 00 00 00 return KILL\n" " 4: 06 00 00 00 00 00 ff 7f return ALLOW\n" #. type: Plain text #: ceccomp.adoc:35 msgid "" "The part presented in hex is what kernel received, and `ceccomp` take it to " "disassemble back to human readable text. For instance the *lineno* in the " "left and *statement* in the right." msgstr "" "以上十六进制的部分就是内核收到的过滤器,而 `ceccomp` 负责把它拿来反汇编为人类" "可读的文本。\n" "例如左侧的 *行号* 和右侧的 *伪代码* 。" #. type: Plain text #: ceccomp.adoc:38 msgid "" "Later I'll use _TEXT_ in short for BPF human readable text, and use _RAW_ in " "short for BPF raw format, please keep that in mind." msgstr "" "之后我会使用 _TEXT_ 作为BPF过滤器人类可读文本(伪代码)的缩写,\n" "使用 _RAW_ 作为BPF过滤器原始格式的缩写,请记住这个约定。" #. type: Title == #: ceccomp.adoc:39 #, no-wrap msgid "DESCRIPTION" msgstr "描述" #. type: Plain text #: ceccomp.adoc:44 msgid "" "`ceccomp` have 5 main functions, basically it's a C version of `seccomp-" "tools`, however, there are some breaking changes you need to know, which " "will be highlighted in each subcommand section." msgstr "" "`ceccomp` 有5个主要的功能,它基本上是C版本的 `seccomp-tools` ,然而,\n" "有一些不同的地方你需要知道,它们会在在每个子命令的章节中被注明。" #. type: Title === #: ceccomp.adoc:45 #, no-wrap msgid "asm - ASSEMBLE" msgstr "asm - 汇编" #. type: Plain text #: ceccomp.adoc:48 #, no-wrap msgid " ceccomp asm [-c WHEN] [-a ARCH] [-f FMT] [TEXT]\n" msgstr " ceccomp asm [-c WHEN] [-a ARCH] [-f FMT] [TEXT]\n" #. type: Plain text #: ceccomp.adoc:51 msgid "" "Assemble _TEXT_ to _RAW_. Use it to embed hand written filter rules into C " "code or to see the original code of some _TEXT_." msgstr "" "将 _TEXT_ 汇编为 _RAW_ 。适用于将手写的过滤器规则嵌入到C代码中,\n" "或希望观察一些 _TEXT_ 对应的原始字节码。" #. type: Labeled list #: ceccomp.adoc:52 ceccomp.adoc:101 ceccomp.adoc:144 ceccomp.adoc:194 #, no-wrap msgid "WHEN" msgstr "WHEN" #. type: Plain text #: ceccomp.adoc:56 msgid "" "Determines when to display warnings and errors in color. If the value is " "_auto_, ceccomp will display color when the output target is a \"tty\". Can " "be _auto_, _never_ or _always_. The default value is _auto_." msgstr "" "决定了 `ceccomp` 何时输出有颜色的文本。当设置为 _auto_ 时, `ceccomp`\n" "会在输出目标是一个“tty”时打印颜色。可以是 _auto_ 、 _never_ 或者 " "_always_ 。\n" "默认值是 _auto_ 。" #. type: Labeled list #: ceccomp.adoc:57 ceccomp.adoc:105 ceccomp.adoc:159 #, no-wrap msgid "ARCH" msgstr "ARCH" #. type: Plain text #: ceccomp.adoc:62 msgid "" "Set to any architecture libseccomp supports. Will be used to determine the " "actual syscall number behind the name (for example, on x86_64, you could " "write `\"execve\"` instead of `59` like the basic example above). Your " "system arch will be taken if not set via `uname`. The default value on your " "system is {ARCH}." msgstr "" "可以设置为任何 libseccomp 支持的架构。它将被用于决定系统调用名称对应的系统调" "用号。\n" "例如,在 x86_64 上,就像上面的基本示例,你可以写 `\"execve\"` 而不是数字 " "`59`\n" "指代系统调用号。如果不设置这个参数,则通过 `uname` 提取当前系统的架构。\n" "你的系统上的默认值是 {ARCH} 。" #. type: Plain text #: ceccomp.adoc:66 msgid "" "Since _version 4.0_, endianness is considered. If target endianness *ARCH* " "is different from machine endianness, the filters will be reversed (CODE and " "K) before outputting." msgstr "" "从 _4.0 版本_ 开始考虑端序。如果目标架构 *ARCH* 的端序与机器端序不同,则会在" "输出前反转过滤器(CODE 和 K)。" #. type: Labeled list #: ceccomp.adoc:67 #, no-wrap msgid "FMT" msgstr "FMT" #. type: Plain text #: ceccomp.adoc:71 msgid "" "Determines how `ceccomp` produces binary-format bpf code. Can be _hexfmt_, " "_hexline_ or _raw_. You could find sample output in <> section. " "The default value is _hexline_." msgstr "" "决定了 `ceccomp` 如何输出二进制格式的BPF字节码。可以是 _hexfmt_ 、 " "_hexline_\n" "或者 _raw_ 。你可以在 <> 节中找到示例输出。默认值是 " "_hexline_ 。" #. type: Labeled list #: ceccomp.adoc:72 ceccomp.adoc:162 #, no-wrap msgid "TEXT" msgstr "TEXT" #. type: Plain text #: ceccomp.adoc:75 msgid "" "Take a optional filename to determine which file containing _TEXT_ will be " "assembled. Will read from _stdin_ if not set. `-` is treated as _stdin_." msgstr "" "一个可选的文件名,其中存放了需要被汇编的 _TEXT_ 。不设置则从 _标准输入_ 中读" "取。`-` 被视作 _标准输入_。" #. type: Plain text #: ceccomp.adoc:78 msgid "" "The assembly syntax was changed greatly since _version 4.0_, please checkout " "grammar reference below!" msgstr "_4.0 版本_ 大幅修改了汇编的语法,请查阅下面的语法参考!" #. type: Plain text #: ceccomp.adoc:81 msgid "" "Please check out <> section to see how to write a " "rule by hand. Some examples will be displayed in <> section." msgstr "" "查看 <> 一节可以找到如何手写规则。一些示例会在 <> 一节中展示。" #. type: Table #: ceccomp.adoc:92 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools asm`\n" "|Use its own grammar to assemble, a bit script like; can assemble invalid _TEXT_\n" "which will be rejected by kernel\n" "\n" "|`ceccomp asm`\n" "|You can just take `disasm` output to `asm`, no new grammar is needed to learn;\n" "take `stdin` as input by default\n" msgstr "" "|命令|差别\n" "\n" "|`seccomp-tools asm`\n" "|使用它自己的语法汇编,有点像脚本;可以汇编会被内核拒绝的错误 _TEXT_\n" "\n" "|`ceccomp asm`\n" "|你可以直接拿着 `disasm` 的输出来汇编,不需要学习新语法;默认使用 _标准输入_ 作为输入\n" #. type: Title === #: ceccomp.adoc:94 #, no-wrap msgid "disasm - DISASSEMBLE" msgstr "disasm - 反汇编" #. type: Plain text #: ceccomp.adoc:97 #, no-wrap msgid " ceccomp disasm [-c WHEN] [-a ARCH] [RAW]\n" msgstr " ceccomp disasm [-c WHEN] [-a ARCH] [RAW]\n" #. type: Plain text #: ceccomp.adoc:100 msgid "" "Disassemble _RAW_ to _TEXT_. Use it to see what does a filter do if you " "could not access filter via `trace` and have to manually extract the filter " "out." msgstr "" "反汇编 _RAW_ 为 _TEXT_ 。适用于当你无法使用 `trace` 看到过滤器时,必须手动提" "取过滤器,\n" "然后检查其含义。" #. type: Plain text #: ceccomp.adoc:104 msgid "" "Argument description can be found in <> section. `disasm` " "may print more text in color including syntax highlighting for _TEXT_." msgstr "" "参数描述可以在 <> 一节中找到。 `disasm` 可能会打印更多有颜色的文" "本,包括针对\n" "_TEXT_ 的语法高亮。" #. type: Plain text #: ceccomp.adoc:110 msgid "" "Set to any architecture libseccomp supports. Will be used to determine how " "filtered syscall number in _RAW_ filter is translated to syscall name (for " "example, on x86_64, the number `0x3b` is translated to `execve` if is " "comparing syscall_nr, see the basic example above). The default value on " "your system is {ARCH}." msgstr "" "可以设置为任何 libseccomp 支持的架构。它将被用于决定 _RAW_ 中的系统调用号如何" "被翻译为系统调用名。\n" "例如,在 x86_64 上,在比较系统调用号时,数字 `0x3b` 将被翻译为 `execve` ,可" "以看上面的基本示例。\n" "你的系统上的默认值是 {ARCH} 。" #. type: Labeled list #: ceccomp.adoc:111 #, no-wrap msgid "RAW" msgstr "RAW" #. type: Plain text #: ceccomp.adoc:114 msgid "" "A binary file with raw BPF codes. Takes _stdin_ as input if not set. Treat `-" "` as _stdin_. The file is arch-revelent, so it may not be portable on " "different archs." msgstr "" "包含原始 BPF 代码的二进制文件。如果不设置则将 _标准输入_ 作为输入。`-` 被视" "作 _标准输入_。这个文件是与架构有关的,所以它在不同架构之间可能不是通用的。" #. type: Plain text #: ceccomp.adoc:118 msgid "" "Since _version 4.0_, endianness is considered. If target endianness *ARCH* " "is different from machine endianness, the filters will be reversed (CODE and " "K) before decoding." msgstr "" "从 _4.0 版本_ 开始考虑端序。如果目标架构 *ARCH* 的端序与机器端序不同,则会在" "解码前反转过滤器(CODE 和 K)。" #. type: Plain text #: ceccomp.adoc:123 msgid "" "ceccomp will try to resolve syscall number under an arch ONLY IF that at " "that line, arch can be determined. On foreign arch (not equal to the arch " "you set), the foreign arch will be prepended to syscall name. You may notice " "that in some cases, seccomp-tools is able to resolve the name while ceccomp " "is not, that may be intended as the arch is not determined." msgstr "" "当且仅当在某一行的架构可以被确定时,ceccomp 才会尝试用那个架构解析那个系统调" "用号。\n" "如果是外部架构(不等于你设置的架构),它会被附加到系统调用名前。你可能会注意" "到在一些情况下,\n" "seccomp-tools 能解析一些系统调用名,而 ceccomp 不能,这可能是因为此时架构不能" "被确定。" #. type: Table #: ceccomp.adoc:133 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools disasm`\n" "|Disassembles in its format; never check if the filter is valid\n" "\n" "|`ceccomp disasm`\n" "|Disassembles in ceccomp format, and takes `stdin` as input by default; check arch strictly\n" "and always display foreign arch name\n" msgstr "" "|命令|差别\n" "\n" "|`seccomp-tools disasm`\n" "|用它自己的语法反汇编;永远不会检查 _RAW_ 是否合法\n" "\n" "|`ceccomp disasm`\n" "|用 ceccomp 语法反汇编,并且默认将 _标准输入_ 作为输入;严格检查架构,\n" "并且永远打印外部架构名\n" #. type: Title === #: ceccomp.adoc:135 #, no-wrap msgid "emu - EMULATE" msgstr "emu - 模拟" #. type: Plain text #: ceccomp.adoc:138 #, no-wrap msgid " ceccomp emu [-c WHEN] [-a ARCH] [-q] TEXT SYSCALL_NAME/SYSCALL_NR [ARGS[0] ARGS[1] ... ARGS[5] PC]\n" msgstr " ceccomp emu [-c WHEN] [-a ARCH] [-q] TEXT SYSCALL_NAME/SYSCALL_NR [ARGS[0] ARGS[1] ... ARGS[5] PC]\n" #. type: Plain text #: ceccomp.adoc:143 msgid "" "Emulate what will happen if `syscall(SYSCALL_NR, ARGS[0], ARGS[1], ..., " "ARGS[5])` from `PC` is called following rules described in _TEXT_. Use it to " "see the result without actually running it in program or you don't want to " "examine the filter rule manually. This subcommand can be used to " "automatically examining a filter." msgstr "" "按照 _TEXT_ 中描述的规则,模拟从 `PC` 调用 `syscall(SYSCALL_NR, ARGS[0], " "ARGS[1], ..., ARGS[5])`\n" "的结果。适用于在不实际运行程序或不想手动检查规则时,查看触发系统调用的结" "果。\n" "这个子命令适合用来自动检测一个过滤器。" #. type: Plain text #: ceccomp.adoc:147 msgid "" "Argument description can be found in <> section. `emu` may " "print more text in color including syntax highlighting for _TEXT_ and " "skipped statements." msgstr "" "参数描述可以在 <> 一节中找到。 `emu` 可能会打印更多有颜色的文本," "包括针对\n" "_TEXT_ 的语法高亮以及跳过的伪代码。" #. type: Labeled list #: ceccomp.adoc:148 #, no-wrap msgid "SYSCALL_NAME/SYSCALL_NR" msgstr "SYSCALL_NAME/SYSCALL_NR" #. type: Plain text #: ceccomp.adoc:153 msgid "" "If you set *SYSCALL_NAME* (like `execve`), it will be translated to " "*SYSCALL_NR* under *ARCH* first. Or else set *SYSCALL_NR* directly (like " "`59`). Then the nr will be tested against the bpf filter to see the result " "of that syscall. This argument is NOT optional." msgstr "" "如果你设置了 *SYSCALL_NAME* (比如 `execve` ),那么它会基于 *ARCH* 先被翻译" "为对应的\n" "*SYSCALL_NR* 。或者你可以直接设置 *SYSCALL_NR* (例如 `59` )。然后会测试这个" "系统调用号经过\n" "BPF 过滤器处理后的输出并打印出来。这个参数是必填的。" #. type: Labeled list #: ceccomp.adoc:154 #, no-wrap msgid "ARGS[0-5] and PC" msgstr "ARGS[0-5] 和 PC" #. type: Plain text #: ceccomp.adoc:158 msgid "" "Register values when calling syscall. For example, on x86_64, these are " "equivalent to `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9` and `rip`. Their " "default value is 0." msgstr "" "当调用系统调用时对应寄存器的值。例如,在 x86_64 上,它们分别对应了\n" "`rdi` 、 `rsi` 、 `rdx` 、 `r10` 、 `r8` 、 `r9` 和 `rip` 。它们的默认值都是" "0。" #. type: Plain text #: ceccomp.adoc:161 msgid "Argument description can be found in <> section." msgstr "参数描述可以在 <> 一节中找到。" #. type: Plain text #: ceccomp.adoc:166 msgid "" "Take a filename to determine which file containing _TEXT_ rule will be " "tested. Note that filename CAN NOT be ignored as ceccomp can not determine " "if a positional argument is syscall or filename. Use `-` to refer to _stdin_." msgstr "" "一个文件名,其中存放了需要被测试的 _TEXT_ 规则。注意文件名 不能 被忽略,因为 " "ceccomp 无法判断一个位置参数是系统调用号还是文件名。使用 `-` 可以指代 _标准输" "入_。" #. type: Labeled list #: ceccomp.adoc:167 ceccomp.adoc:219 #, no-wrap msgid "-q, --quiet" msgstr "-q, --quiet" #. type: Plain text #: ceccomp.adoc:170 msgid "" "Only print the eval result of the filter. For example, if last statement " "emulated is `return KILL`, then `KILL` is printed." msgstr "" "只打印过滤器的模拟结果。例如,最后一行模拟的伪代码是 `return KILL`,那么将会" "打印 `KILL`。" #. type: Table #: ceccomp.adoc:180 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools emu`\n" "|Take a _RAW_ as input\n" "\n" "|`ceccomp emu`\n" "|Take a _TEXT_ as input and take `stdin` as input by default; set *PC* is\n" "possible\n" msgstr "" "|命令|差别\n" "\n" "|`seccomp-tools emu`\n" "|用 _RAW_ 作为输入\n" "\n" "|`ceccomp emu`\n" "|用 _TEXT_ 作为输入,并默认将 _标准输入_ 作为输入;可以设置 *PC*\n" #. type: Title === #: ceccomp.adoc:182 #, no-wrap msgid "trace - TRACE FILTER IN RUNTIME" msgstr "trace - 运行时捕获过滤器" #. type: Plain text #: ceccomp.adoc:186 #, no-wrap msgid "" " ceccomp trace [-c WHEN] [-q] [-o FILE] PROGRAM [program-args]\n" " [-c WHEN] [-q] -p PID [-s]\n" msgstr "" " ceccomp trace [-c WHEN] [-o FILE] PROGRAM [program-args]\n" " [-c WHEN] -p PID [-s]\n" #. type: Plain text #: ceccomp.adoc:193 msgid "" "The first line captures filters *PROGRAM* loads in runtime by tracing it; " "the second line extract seccomp filters from *PID*, or trace *PID* to " "capture subsequent seccomp filters; once fetched filters, print them in " "_TEXT_. You can only choose one of the two formats above. Use this if " "running the program is the simplest way to fetch bpf filters or a program " "with seccomp filters installed is waiting for input." msgstr "" "使用第一行的命令可以利用调试在 *PROGRAM* 运行中加载过滤器时动态捕获过滤器;\n" "第二行的命令可以从 *PID* 对应的进程中提取出 seccomp 过滤器,或通过调试 *PID* " "捕获后续的 seccomp 过滤器;一旦捕获到了过滤器,\n" "将会以 _TEXT_ 的格式将它打印出来。你可以从两个格式中选择一个使用。\n" "适用于运行一个程序是捕获BPF过滤器最简单的方式或者一个安装了 seccomp\n" "过滤器的程序正在等待输入。" #. type: Plain text #: ceccomp.adoc:197 msgid "" "Argument description can be found in <> section. `trace` may " "print more text in color including syntax highlighting for _TEXT_." msgstr "" "参数描述可以在 <> 一节中找到。 `trace` 可能会打印更多有颜色的文" "本,包括针对\n" "_TEXT_ 的语法高亮。" #. type: Labeled list #: ceccomp.adoc:198 #, no-wrap msgid "FILE" msgstr "FILE" #. type: Plain text #: ceccomp.adoc:203 msgid "" "May be useful when *PROGRAM* produces quite a lot output in _stderr_. " "`ceccomp` allow user to close _stdin_ and _stdout_ to limit *PROGRAM* input " "and output, so `ceccomp` use _stderr_ to print messages when running " "*PROGRAM*, set *FILE* if you want to see _TEXT_ in some other file. Treat `-" "` as _stdout_." msgstr "" "当 *PROGRAM* 会产生很多输出到 _标准错误_ 时可能很有用。 `ceccomp` 允许用户关" "闭\n" "_标准输入_ 和 _标准输出_ 来限制 *PROGRAM* 的输入和输出,所以 当运行\n" "*PROGRAM* 时 `ceccomp` 使用 _标准错误_ 来打印消息。如果你想在别的文件中看见\n" "_TEXT_ 的话请设置 *FILE* 。`-` 被视作 _标准输出_。" #. type: Labeled list #: ceccomp.adoc:204 #, no-wrap msgid "PROGRAM" msgstr "PROGRAM" #. type: Plain text #: ceccomp.adoc:207 msgid "" "Set to the program you want to run, and *program-args* are its arguments " "just like running shell command `exec PROGRAM program-args`." msgstr "" "设置为你想运行的程序,并且 *program-args* 将作为它的参数,\n" "就像运行 shell 命令 `exec PROGRAM program-args` 。" #. type: Labeled list #: ceccomp.adoc:208 #, no-wrap msgid "PID" msgstr "PID" #. type: Plain text #: ceccomp.adoc:213 msgid "" "Set to the pid you want to inspect. *PID* is conflict with *PROGRAM*; you " "could either run a program dynamically or examine a pid in one command. " "Without `-s` flag, trace pid will try to extract seccomp filter in *PID* via " "`ptrace(PTRACE_SECCOMP_GET_FILTER)`, which may not be available in some " "systems." msgstr "" "设置为你想检查的 pid。 *PID* 和 *PROGRAM* 相冲突;你只能在一条命令中动态运行" "一个程序,\n" "或者检查一个 pid。没有 `-s` 标志,trace pid 会尝试使用 " "`ptrace(PTRACE_SECCOMP_GET_FILTER)` 从 *PID* 中提取 seccomp 过滤器,这个操作" "在一些系统上可能不可用。" #. type: Labeled list #: ceccomp.adoc:214 #, no-wrap msgid "-s, --seize" msgstr "-s, --seize" #. type: Plain text #: ceccomp.adoc:218 msgid "" "*ONLY AVAILABLE FOR TRACE PID MODE.* Set this flag will override trace pid " "behavior to attach to *PID* and keep tracing for seccomp filter loading like " "trace prog mode. _This flag was introduced in version 4.0._" msgstr "" "*只适用于 TRACE PID 模式。* 设置这个标志将会覆盖 trace pid 的行为为像 trace " "prog 模式一样把调试器挂到 *PID* 上并持续跟踪 seccomp 过滤器的加载。_这个标志" "引入于 4.0 版本。_" #. type: Plain text #: ceccomp.adoc:222 msgid "" "Set to suppress the extra *[INFO]* prints when detect process forking, " "exiting or seccomp filter loading. _This flag was introduced in version 4.0._" msgstr "" "设置这个标志将会在检测到进程分叉、退出或加载 seccomp 过滤器时抑制多余的 " "*[INFO]* 输出。 _这个标志引入于 4.0 版本。_" #. type: Plain text #: ceccomp.adoc:226 msgid "" "To extract filters from *PID*, `CAP_SYS_ADMIN` is needed (without `-s` " "flag) and `CAP_SYS_PTRACE` may also be needed, the easiest way to acquire " "them is calling `ceccomp` with `sudo`." msgstr "" "要想从 *PID* 中提取过滤器,你需要 `CAP_SYS_ADMIN` (没有 `-s` 标志),同时还" "可能需要\n" "`CAP_SYS_PTRACE` ,最简单的获取它们的方法是用 `sudo` 运行 `ceccomp` 。" #. type: Plain text #: ceccomp.adoc:230 msgid "" "Since _version 3.1_, multiple process tracing is introduced, and when tracee " "forking/resolving/exiting, an extra INFO message is printed. You can discard " "it by running command like `ceccomp trace -q PROG 2>/dev/null`." msgstr "" "从 _3.1 版本_ 开始引入了多进程支持,并且当被调试进程 fork/resolve/exit\n" "时,将会打印一条额外的 INFO 信息。你可以使用像 `ceccomp trace -q PROG 2>/dev/" "null`\n" "这样的命令丢弃它。" #. type: Table #: ceccomp.adoc:243 #, no-wrap msgid "" "|Command|Difference\n" "\n" "|`seccomp-tools dump`\n" "|Setting output format is possible; each filter can be output to a different\n" "file; killing *PROGRAM* once *LIMIT* times of filters loaded; wrapping *PROGRAM*\n" "in `sh -c`\n" "\n" "|`ceccomp trace`\n" "|All filters are output to a single file; never kill *PROGRAM*; *PROGRAM* is\n" "launched directly, so `./` is not needed; explicitly print when forking;\n" "able to attach to pid for dynamic seccomp filter capturing\n" msgstr "" "|命令|差别\n" "\n" "|`seccomp-tools dump`\n" "|可以设置输出格式;每一个过滤器可以输出到不同的文件;当 *PROGRAM*\n" "加载了 *LIMIT* 个过滤器后就杀死程序;将 *PROGRAM* 包装在 `sh -c` 中运行\n" "\n" "|`ceccomp trace`\n" "|所有过滤器被输出到同一个文件;永远不会杀死 *PROGRAM* ; *PROGRAM* 是直接被执行的,\n" "所以不需要 `./` ;当 fork 时,显式打印事件;能够附加调试器到 pid 上动态捕捉 seccomp 过滤器\n" #. type: Title === #: ceccomp.adoc:245 #, no-wrap msgid "probe - TEST COMMON SYSCALLS INSTANTLY" msgstr "probe - 快速测试常见的系统调用" #. type: Plain text #: ceccomp.adoc:248 #, no-wrap msgid " ceccomp probe [-c WHEN] [-o FILE] [-q] PROGRAM [program-args]\n" msgstr " ceccomp probe [-c WHEN] [-o FILE] [-q] PROGRAM [program-args]\n" #. type: Plain text #: ceccomp.adoc:252 msgid "" "Run *PROGRAM* with *program-args* to captures *FIRST* seccomp filter, and " "then kill all children. Use it when a quick check against a program is " "needed, and detect potential seccomp rule issues." msgstr "" "以 *program-args* 为参数运行 *PROGRAM* 来捕获 *第一个* seccomp 过滤器,\n" "然后杀死所有子进程。适用于快速测试一个程序的规则并检测潜在的 seccomp\n" "规则问题。" #. type: Plain text #: ceccomp.adoc:254 msgid "" "All argument descriptions can be found in <> section." msgstr "所有参数描述都可以在 <> 一节中找到。" #. type: Plain text #: ceccomp.adoc:258 msgid "" "The output for this subcommand is the emulating result of common syscalls " "like `execve`, `open` and so on. If the filter itself is not capable of " "blocking syscalls, you could know that with a glance." msgstr "" "这个子命令的输出是一系列常见的系统调用的模拟结果,例如 `execve` 、 `open` " "等。\n" "如果过滤器本身并不能阻拦系统调用,那你一眼就能看出来。" #. type: Plain text #: ceccomp.adoc:261 msgid "" "Typical output for this subcommand is described below, more detailed example " "could be found in <> section." msgstr "" "这个子命令的典型输出如下所示,更多完整的实例可以在 <> 一节中找" "到。" #. type: Plain text #: ceccomp.adoc:273 #, no-wrap msgid "" " open -> ALLOW\n" " read -> ALLOW\n" " write -> ALLOW\n" " execve -> KILL\n" " execveat -> KILL\n" " mmap -> ALLOW\n" " mprotect -> ALLOW\n" " openat -> ALLOW\n" " sendfile -> ALLOW\n" " ptrace -> ERRNO(1)\n" " fork -> ALLOW\n" msgstr "" " open -> ALLOW\n" " read -> ALLOW\n" " write -> ALLOW\n" " execve -> KILL\n" " execveat -> KILL\n" " mmap -> ALLOW\n" " mprotect -> ALLOW\n" " openat -> ALLOW\n" " sendfile -> ALLOW\n" " ptrace -> ERRNO(1)\n" " fork -> ALLOW\n" #. type: Plain text #: ceccomp.adoc:275 msgid "`seccomp-tools` don't have this subcommand." msgstr "`seccomp-tools` 没有等价的子命令。" #. type: Title == #: ceccomp.adoc:276 #, no-wrap msgid "TEXT GRAMMAR REFERENCE" msgstr "TEXT 语法参考" #. type: Plain text #: ceccomp.adoc:283 msgid "" "The grammar changed greatly since _version 4.0_ as we refactored lexer for " "better human readability. The wrapper now prefixed by `#` as it's a comment " "now. And _line_ is replaced by _label_, so now lexer depends on label " "declaration to decide where to jump, instead of lineno in _TEXT_ file." msgstr "" "_4.0 版本_ 提高了词法分析器代码的可读性,伴随着大幅修改的语法。前缀为 `#` 的" "行现在是注释了。同时 _行号_ 被替换为 _标签_,现在词法分析器根据标签定义来决定" "跳转到哪里,而不是根据 _TEXT_ 文件中的行号。" #. type: Plain text #: ceccomp.adoc:288 msgid "" "A valid _TEXT_ format is described in EBNF-like declaration here: https://" "github.com/dbgbgtf1/Ceccomp/issues/17#issuecomment-3610531705. If you have " "no interest to know what EBNF is, please keep reading for examples." msgstr "" "这里有正确的 _TEXT_ 格式,以类 EBNF 语法描述:https://github.com/dbgbgtf1/" "Ceccomp/issues/17#issuecomment-3610531705。\n" "没兴趣了解 EBNF?请继续阅读以下的例子。" #. type: Plain text #: ceccomp.adoc:290 msgid "BPF ops which are not described below are banned by kernel." msgstr "其余未描述到的BPF操作都被内核禁止了。" #. type: Title === #: ceccomp.adoc:291 #, no-wrap msgid "Comment and Label" msgstr "注释与标签" #. type: Plain text #: ceccomp.adoc:295 msgid "" "`ceccomp disasm` displays a lot of things, but some of them are optional for " "asm." msgstr "`ceccomp disasm` 展示了很多东西,但对于 asm 来说有些是可选的。" #. type: Plain text #: ceccomp.adoc:300 #, no-wrap msgid "" " #Label CODE JT JF K\n" " #---------------------------------\n" " L0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW\n" " #---------------------------------\n" msgstr "" " #Label CODE JT JF K\n" " #---------------------------------\n" " L0001: 0x06 0x00 0x00 0x7fff0000 return ALLOW\n" " #---------------------------------\n" "\n" #. type: Plain text #: ceccomp.adoc:302 msgid "Any text after `#` will be discarded by asm like some script languages." msgstr "任何在 `#` 之后的文本将被 asm 丢弃,就像一些脚本语言一样。" #. type: Plain text #: ceccomp.adoc:304 msgid "Empty lines are accepted." msgstr "允许空行。" #. type: Plain text #: ceccomp.adoc:312 msgid "" "Label declaration is an identifier at the beginning of line and suffixed by " "`:` like `L0001`. An identifier is a string starts with alpha and contains " "with only alphanumeric characters and underscore `_`. Label is only " "necessary if it's the destination of `goto`, these redundant labels added by " "disasm are for readability. E.g. in `if ($A == 0) goto somewhere`, " "`somewhere` is a label and must be declared after the statement. Label " "declaration can take a line separately, or be put in front of statement." msgstr "" "标签声明是一个从行首开始并以 `:` 结尾的标识符,例如 `L0001`。标识符是一个以字" "母为首,中间只包含字母、数字和下划线 `_` 的字符串。标签只在它是 `goto` 的目标" "时才是必要的,由 disasm 添加的多余的标签知识为了可读性。例如在 `if ($A == 0) " "goto somewhere` 中,`somewhere` 是一个标签并且必须在这行伪代码后声明。标签声" "明可以单独占据一行,也可以放置在伪代码的前面。" #. type: Plain text #: ceccomp.adoc:315 msgid "" "The `CODE`, `JT`, `JF` and `K` value generated by disasm will be discarded " "by asm, asm only parse the effective statement after `K`." msgstr "" "由 disasm 生成的 `CODE`、`JT`、`JF` 和 `K` 值会被 asm 丢弃,asm 之解析 `K` 之" "后的有效伪代码。" #. type: Plain text #: ceccomp.adoc:320 msgid "" "There are some slight difference between `ceccomp disasm` and `seccomp-tools " "disasm`, down below is a general example. And some statements are different, " "so don't pipe seccomp-tools output to ceccomp blindly." msgstr "" "`ceccomp disasm` 和 `seccomp-tools disasm` 的输出之间有很多细微的差别,\n" "以下是一个典型的输出示例。同时有些伪代码是不同的,所以不要盲目将 seccomp-" "tools\n" "的输出管道给 ceccomp。" #. type: Plain text #: ceccomp.adoc:324 #, no-wrap msgid "" " line CODE JT JF K\n" " =================================\n" " 0000: 0x06 0x00 0x00 0x7fff0000 return ALLOW\n" msgstr "" " line CODE JT JF K\n" " =================================\n" " 0000: 0x06 0x00 0x00 0x7fff0000 return ALLOW\n" #. type: Title === #: ceccomp.adoc:325 #, no-wrap msgid "Assignment" msgstr "赋值" #. type: Plain text #: ceccomp.adoc:329 msgid "" "`A` can be set to seccomp attributes directly. But `X` can not be assigned " "with seccomp attributes directly due to kernel limit." msgstr "" "`A` 可以直接赋值为 seccomp 属性。由于内核限制, `X` 不能直接赋值为 seccomp 属" "性。" #. type: Plain text #: ceccomp.adoc:332 #, no-wrap msgid "" " $A = $arch\n" " $A = $syscall_nr\n" msgstr "" " $A = $arch\n" " $A = $syscall_nr\n" #. type: Plain text #: ceccomp.adoc:334 msgid "" "To assign `A` with those 64-bit long fields, `low_` or `high_` prefix is " "needed." msgstr "要给 `A` 赋值为这些64位长的字段,必须使用 `low_` 或者 `high_` 的前缀。" #. type: Plain text #: ceccomp.adoc:342 #, no-wrap msgid "" " $A = $low_pc\n" " $A = $high_pc\n" " $A = $low_args[0]\n" " $A = $high_args[0]\n" " ...\n" " $A = $low_args[5]\n" " $A = $high_args[5]\n" msgstr "" " $A = $low_pc\n" " $A = $high_pc\n" " $A = $low_args[0]\n" " $A = $high_args[0]\n" " ...\n" " $A = $low_args[5]\n" " $A = $high_args[5]\n" #. type: Plain text #: ceccomp.adoc:345 msgid "" "A special attribute is `sizeof(struct seccomp_data)`, that can be assigned " "to `A` or `X` directly." msgstr "" "一个特殊的属性是 `sizeof(struct seccomp_data)` ,它可以直接赋值给 `A` 或 " "`X` 。" #. type: Plain text #: ceccomp.adoc:348 #, no-wrap msgid "" " $A = $scmp_data_len\n" " $X = $scmp_data_len\n" msgstr "" " $A = $scmp_data_len\n" " $X = $scmp_data_len\n" #. type: Plain text #: ceccomp.adoc:352 msgid "" "Temporary memory is 32-bit, to access them, you could use hex or dec as " "index. Both `A` and `X` is assignable. Assigning immediate values to `A` or " "`X` accepts any format of number if you imply the correct base by \"0x\" or " "\"0b\"." msgstr "" "临时内存是32位的,要想访问它们,你可以使用十六进制或者十进制的索引。\n" "`A` 和 `X` 都是可赋值的。给 `A` 或 `X` 赋值为立即数接受任意格式的数字,只要你" "使用 \"0x\" 或者 \"0b\" 等前缀正确表达数字是几进制的。" #. type: Plain text #: ceccomp.adoc:360 #, no-wrap msgid "" " $X = $mem[0]\n" " $A = $mem[0xf]\n" " $A = $mem[15] # both hex and dec index are OK\n" " $A = 0\n" " $X = 0x3b\n" " $A = 0b1111\n" " $A = 0333\n" msgstr "" " $X = $mem[0]\n" " $A = $mem[0xf]\n" " $A = $mem[15] # both hex and dec index are OK\n" " $A = 0\n" " $X = 0x3b\n" " $A = 0b1111\n" " $A = 0333\n" #. type: Plain text #: ceccomp.adoc:363 msgid "" "You could also assign `X` to `A` or in the reverse order. Assign `X` or `A` " "to temporary memory is definitely okay." msgstr "" "你还可以将 `X` 赋值给 `A` 或者反过来。将 `X` 或 `A` 赋值给临时内存当然可以。" #. type: Plain text #: ceccomp.adoc:368 #, no-wrap msgid "" " $A = $X\n" " $X = $A\n" " $mem[3] = $X\n" " $mem[0x4] = $A\n" msgstr "" " $A = $X\n" " $X = $A\n" " $mem[3] = $X\n" " $mem[0x4] = $A\n" #. type: Title === #: ceccomp.adoc:369 #, no-wrap msgid "Arithmetic Operations" msgstr "数学运算" #. type: Plain text #: ceccomp.adoc:372 msgid "Various operations can be applied to `A`." msgstr "你可以以多种方式操作 `A` 。" #. type: Plain text #: ceccomp.adoc:379 #, no-wrap msgid "" " $A += 30\n" " $A -= 4\n" " $A *= 9\n" " $A /= 1\n" " $A &= 7\n" " $A >>= 6\n" msgstr "" " $A += 30\n" " $A -= 4\n" " $A *= 9\n" " $A /= 1\n" " $A &= 7\n" " $A >>= 6\n" #. type: Plain text #: ceccomp.adoc:381 msgid "The right value can be `X`." msgstr "右值也可以是 `X` 。" #. type: Plain text #: ceccomp.adoc:386 #, no-wrap msgid "" " $A &= $X\n" " $A |= $X\n" " $A ^= $X\n" " $A <<= $X\n" msgstr "" " $A &= $X\n" " $A |= $X\n" " $A ^= $X\n" " $A <<= $X\n" #. type: Plain text #: ceccomp.adoc:388 msgid "And there is a way to negativate `A`." msgstr "想要对 `A` 取反可以这么做。" #. type: Plain text #: ceccomp.adoc:390 #, no-wrap msgid " $A = -$A\n" msgstr " $A = -$A\n" #. type: Title === #: ceccomp.adoc:391 #, no-wrap msgid "Jump Downwards If ..." msgstr "当...时向下跳转" #. type: Plain text #: ceccomp.adoc:394 msgid "Unconditional jump:" msgstr "无条件跳转:" #. type: Plain text #: ceccomp.adoc:396 #, no-wrap msgid " goto L3\n" msgstr " goto L3\n" #. type: Plain text #: ceccomp.adoc:398 msgid "Jump if:" msgstr "当...跳转:" #. type: Plain text #: ceccomp.adoc:404 #, no-wrap msgid "" " if ($A == execve) goto L3\n" " if ($A != 1234) goto L4\n" " if ($A & $X) goto L5\n" " if !($A & 7) goto L6\n" " if ($A <= $X) goto L7\n" msgstr "" " if ($A == execve) goto L3\n" " if ($A != 1234) goto L4\n" " if ($A & $X) goto L5\n" " if !($A & 7) goto L6\n" " if ($A <= $X) goto L7\n" #. type: Plain text #: ceccomp.adoc:406 msgid "If true jump to ... if false jump to...:" msgstr "当条件为真时跳转到...,条件为假时跳转到...:" #. type: Plain text #: ceccomp.adoc:409 #, no-wrap msgid "" " if ($A > $X) goto L3, else goto L4\n" " if ($A >= 4567) goto L5, else goto L6\n" msgstr "" " if ($A > $X) goto L3, else goto L4\n" " if ($A >= 4567) goto L5, else goto L6\n" #. type: Plain text #: ceccomp.adoc:416 msgid "" "ONLY in conditions, you CAN replace number with syscall name or arch name. " "In example above, `0x3b` is replaced by `execve`. All the syscall name will " "be resolved to syscall number under your selected arch. If you want to " "resolve a syscall name in foreign arch (not equal to your selected arch), " "please prepend a arch and dot. For example, your arch is x86_64, and you are " "writing _aarch64_ rules, then please write like:" msgstr "" "*只有* 在做条件判断时,你才能将数字替换为系统调用号或架构名。在以上的例子" "中, `0x3b`\n" "被 `execve` 替换。所有系统调用名将会以你设置的架构解析为系统调用号。\n" "如果你希望解析外部架构(不等于你设置的架构)的系统调用名,\n" "请在前面附加架构名和一个点。例如,你设置的架构是 x86_64,并且你正在写\n" "_aarch64_ 架构的规则,请这样写:" #. type: Plain text #: ceccomp.adoc:418 #, no-wrap msgid " if ($A == aarch64.read) goto 5\n" msgstr " if ($A == aarch64.read) goto 5\n" #. type: Plain text #: ceccomp.adoc:421 msgid "" "Note that if you manually set arch to _aarch64_ with `-a aarch64`, you can " "omit `aarch64.` in statement." msgstr "" "注意当你手动使用 `-a aarch64` 将架构设置为 _aarch64_ 时,\n" "你可以在伪代码中忽略 `aarch64.` 。" #. type: Title === #: ceccomp.adoc:422 #, no-wrap msgid "Return Code" msgstr "返回码" #. type: Plain text #: ceccomp.adoc:425 msgid "Return value of register `A`:" msgstr "返回寄存器 `A` 的值:" #. type: Plain text #: ceccomp.adoc:427 #, no-wrap msgid " return $A\n" msgstr " return $A\n" #. type: Plain text #: ceccomp.adoc:431 msgid "" "Or return a immediate value, with extra field in `()`. Actions including " "`TRACE`, `TRAP` and `ERRNO` accept an extra field, without `()`, they are " "treated as `action(0)`." msgstr "" "或者返回一个立即数,多余的字段放在 `()` 里。 `TRACE` 、 `TRAP` 和 `ERRNO`\n" "接受一个额外的字段,如果没有 `()` ,它们将被视为 `行为(0)` 。" #. type: Plain text #: ceccomp.adoc:441 #, no-wrap msgid "" " return 0x13371337\n" " return KILL\n" " return KILL_PROCESS\n" " return TRAP(123)\n" " return ERRNO(0)\n" " return TRACE\n" " return TRACE(3)\n" " return LOG\n" " return NOTIFY\n" msgstr "" " return 0x13371337\n" " return KILL\n" " return KILL_PROCESS\n" " return TRAP(123)\n" " return ERRNO(0)\n" " return TRACE\n" " return TRACE(3)\n" " return LOG\n" " return NOTIFY\n" #. type: Title === #: ceccomp.adoc:442 #, no-wrap msgid "Short Example" msgstr "简短的例子" #. type: Plain text #: ceccomp.adoc:446 msgid "" "The following _TEXT_ is valid for asm, which blocks `execve` and `execveat` " "for amd64 syscalls:" msgstr "" "下面的 _TEXT_ 对与 asm 来说是正确的,这段 _TEXT_ 阻止了 amd64 的 `execve` 和 " "`execveat` 系统调用:" #. type: Plain text #: ceccomp.adoc:452 #, no-wrap msgid "" " $A = $syscall_nr\n" " if ($A == execve) goto forbid\n" " if ($A == execveat) goto forbid\n" " return ALLOW\n" " forbid: return KILL\n" msgstr "" " $A = $syscall_nr\n" " if ($A == execve) goto forbid\n" " if ($A == execveat) goto forbid\n" " return ALLOW\n" " forbid: return KILL\n" #. type: Title == #: ceccomp.adoc:453 #, no-wrap msgid "RESTRICTIONS" msgstr "限制" #. type: Plain text #: ceccomp.adoc:456 msgid "Ceccomp asm put some restrictions on _TEXT_ for better performance." msgstr "为了更好的性能,Ceccomp asm 对 _TEXT_ 有一些限制。" #. type: Plain text #: ceccomp.adoc:458 msgid "`'\\0'` must not be found in _TEXT_ since it's a text file." msgstr "由于 _TEXT_ 是个文本文件,`'\\0'` 不能出现在 _TEXT_ 中。" #. type: Plain text #: ceccomp.adoc:459 msgid "A line must be shorter than 384 *bytes*." msgstr "一行必须短于 384 *字节*。" #. type: Plain text #: ceccomp.adoc:460 msgid "A _TEXT_ file must have less than 4096 lines." msgstr "一个 _TEXT_ 文件必须短于 4096 行。" #. type: Plain text #: ceccomp.adoc:461 msgid "A _TEXT_ file must be smaller than 1 MiB." msgstr "一个 _TEXT_ 文家必须小于 1 MiB。" #. type: Plain text #: ceccomp.adoc:464 msgid "" "And for both asm and disasm, effective statements (that can be encoded or " "decoded into BPF) must be less or equal than 1024, this is enforced by " "kernel." msgstr "" "并且对于 asm 和 disasm 来说,有效的伪代码(能被编码或解码为 BPF 的)必须少于" "等于 1024 条,这是内核规定的。" #. type: Plain text #: ceccomp.adoc:467 msgid "" "A fun fact about ceccomp asm: any basic ANSI color in _TEXT_ file, e.g., " "`\\x1b[31m`, will be discarded when processing." msgstr "" "一个关于 ceccomp asm 有趣的知识:任何在 _TEXT_ 文件中基本的 ANSI 颜色,例如 " "`\\x1b[31m`,会在处理时被丢弃。" #. type: Title == #: ceccomp.adoc:468 #, no-wrap msgid "EXAMPLES" msgstr "ceccomp 示例" #. type: Plain text #: ceccomp.adoc:473 msgid "" "Manpage can not display images, so please check out html version of this " "page to see examples." msgstr "手册不能显示图片,因此如果想看示例请参阅html版本。" #. type: Title === #: ceccomp.adoc:476 #, no-wrap msgid "asm example" msgstr "asm 示例" #. type: Target for macro image #: ceccomp.adoc:477 #, no-wrap msgid "asm.webp" msgstr "asm.webp" #. type: Title === #: ceccomp.adoc:478 #, no-wrap msgid "disasm example" msgstr "disasm 示例" #. type: Target for macro image #: ceccomp.adoc:479 #, no-wrap msgid "disasm.webp" msgstr "disasm.webp" #. type: Title === #: ceccomp.adoc:480 #, no-wrap msgid "emu example" msgstr "emu 示例" #. type: Target for macro image #: ceccomp.adoc:481 #, no-wrap msgid "emu.webp" msgstr "emu.webp" #. type: Title === #: ceccomp.adoc:482 #, no-wrap msgid "trace example" msgstr "trace 示例" #. type: Plain text #: ceccomp.adoc:484 msgid "Running program:" msgstr "运行程序:" #. type: Target for macro image #: ceccomp.adoc:485 #, no-wrap msgid "trace.webp" msgstr "trace.webp" #. type: Plain text #: ceccomp.adoc:488 msgid "If set `-o FILE`:" msgstr "如果设置了 `-o FILE` :" #. type: Target for macro image #: ceccomp.adoc:489 #, no-wrap msgid "output_trick.webp" msgstr "output_trick.webp" #. type: Plain text #: ceccomp.adoc:492 msgid "Trace pid mode:" msgstr "PID 模式:" #. type: Target for macro image #: ceccomp.adoc:493 #, no-wrap msgid "trace_pid.webp" msgstr "trace_pid.webp" #. type: Plain text #: ceccomp.adoc:496 msgid "Seize pid mode:" msgstr "跟踪 PID 模式:" #. type: Target for macro image #: ceccomp.adoc:497 #, no-wrap msgid "trace_seize.webp" msgstr "trace_seize.webp" #. type: Plain text #: ceccomp.adoc:500 msgid "Completion for pid mode is available under zsh:" msgstr "zsh下PID模式可以使用补全:" #. type: Target for macro image #: ceccomp.adoc:501 #, no-wrap msgid "trace_completion.webp" msgstr "trace_completion.webp" #. type: Title === #: ceccomp.adoc:503 #, no-wrap msgid "probe example" msgstr "probe 示例" #. type: Target for macro image #: ceccomp.adoc:504 #, no-wrap msgid "probe.webp" msgstr "probe.webp" #. type: Title == #: ceccomp.adoc:507 #, no-wrap msgid "REPO" msgstr "仓库" #. type: Plain text #: ceccomp.adoc:511 msgid "" "Visit https://github.com/dbgbgtf1/Ceccomp to find the code. Pull Requests " "and Issues are welcome!" msgstr "" "在 https://github.com/dbgbgtf1/Ceccomp 可以找到源代码。\n" "欢迎提交 Pull Requests 和 Issues !" #. type: Plain text #: ceccomp.adoc:512 msgid "Copyright (C) 2025-present, distributed under GPLv3 or later." msgstr "Copyright (C) 2025-现在,基于 GPLv3 或更新版本分发。" ceccomp-4.0/docs/po4a.cfg000066400000000000000000000001121514205130000152230ustar00rootroot00000000000000[po_directory] po/ [type: asciidoc] ceccomp.adoc $lang:ceccomp.$lang.adoc ceccomp-4.0/include/000077500000000000000000000000001514205130000144005ustar00rootroot00000000000000ceccomp-4.0/include/asm.h000066400000000000000000000002461514205130000153330ustar00rootroot00000000000000#ifndef ASM_H #define ASM_H #include "utils/parse_args.h" #include extern void assemble (FILE *fp, uint32_t scmp_arch, print_mode_t print_mode); #endif ceccomp-4.0/include/config.h.in000066400000000000000000000004341514205130000164240ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H #define CECCOMP_VERSION "@@VERSION@@" #define CECCOMP_TAG_TIME "@@TAG_TIME@@" #define CECCOMP_BUILDER "@@BUILDER@@" #ifndef LOCALEDIR @@LOCALEDIR@@ #endif #include "i18n.h" #define M_VERSION_FORMAT _ ("ceccomp %s\nTag at %s\nBuild by %s\n") #endif ceccomp-4.0/include/decoder/000077500000000000000000000000001514205130000160055ustar00rootroot00000000000000ceccomp-4.0/include/decoder/check_prog.h000066400000000000000000000002251514205130000202610ustar00rootroot00000000000000#ifndef CHECK_PROG_H #define CHECK_PROG_H #include "main.h" #include #include extern bool check_prog (fprog *prog); #endif ceccomp-4.0/include/decoder/decoder.h000066400000000000000000000006461514205130000175710ustar00rootroot00000000000000#ifndef DECODER_H #define DECODER_H #include "lexical/parser.h" #include "lexical/token.h" #include "main.h" #include "utils/vector.h" #include /** * prog: filters read from input * v: initialized statement vector for decoded statements * Returns true if found any error */ extern bool decode_filters (fprog *prog, vector_t *v); extern token_type decode_return_k (obj_t *ret_obj, uint32_t k); #endif ceccomp-4.0/include/decoder/formatter.h000066400000000000000000000007551514205130000201700ustar00rootroot00000000000000#ifndef FORMATTER_H #define FORMATTER_H #include "lexical/parser.h" #include #define DEFAULT_LABEL "L%04d" typedef void (*print_fn) (obj_t *obj); typedef struct { print_fn handler; char *color; } obj_print_t; extern void extern_obj_printer (FILE *output_fp, obj_t *obj); extern void print_as_comment (FILE *output_fp, const char *comment_fmt, ...) __attribute__ ((format (printf, 2, 3))); extern void print_statement (FILE *output_fp, statement_t *statement); #endif ceccomp-4.0/include/disasm.h000066400000000000000000000004041514205130000160270ustar00rootroot00000000000000#ifndef DISASM_H #define DISASM_H #include "main.h" #include #include extern void print_prog (uint32_t scmp_arch, fprog *prog, FILE *output_fp); extern void disasm (FILE *fp, uint32_t scmp_arch); extern filter g_filters[1025]; #endif ceccomp-4.0/include/emu.h000066400000000000000000000004051514205130000153360ustar00rootroot00000000000000#ifndef EMU_H #define EMU_H #include "utils/parse_args.h" #include "utils/vector.h" extern void emulate_v (vector_t *text_v, vector_t *code_ptr_v, emu_arg_t *emu_arg, FILE *output_fp); extern void emulate (emu_arg_t *emu_arg); #endif ceccomp-4.0/include/help.h000066400000000000000000000075701514205130000155120ustar00rootroot00000000000000#ifndef HELP_H #define HELP_H #include "i18n.h" #define M_CECCOMP_USAGE _ ("Usage: ceccomp \n") #define ASM_HINT "ceccomp asm [ -c WHEN ] [ -a ARCH ] [ -f FMT ] [ text ]" #define DISASM_HINT "ceccomp disasm [ -c WHEN ] [ -a ARCH ] [ raw ]" #define EMU_HINT \ "ceccomp emu [ -c WHEN ] [ -a ARCH ] [ -q ] text syscall_nr [ " \ "args[0-5] ip ]" #define PROBE_HINT \ "ceccomp probe [ -c WHEN ] [ -o FILE ] [ -q ] PROGRAM [ program-args ]" #define TRACE_HINT \ "ceccomp trace [ -c WHEN ] [ -o FILE ] [ -q ] PROGRAM [ program-args ]\n" \ " [ -c WHEN ] [ -s ] [ -q ] -p PID" #define HELP_HINT "ceccomp help" #define VERSION_HINT "ceccomp version" #define M_SUBCMD_HINT \ _ ("asm -- Assemble bpf text to raw bytes\n" \ "disasm -- Disassemble raw bytes to bpf text\n" \ "emu -- Emulate bpf program with given syscall and bpf text\n" \ "help -- Display ceccomp help information\n" \ "probe -- Trace the program for the first filter and emulate common " \ "syscalls\n" \ "trace -- Run program or trace pid, extract bpf filter and then " \ "print to text\n" \ "version -- Display ceccomp version\n") #define M_OPTION_HINT \ _ ("Options:\n" \ "-a, --arch (x86_64|aarch64|...) Which architecture to resolve " \ "syscall_nr, read or write bytestream by the byteorder of arch, " \ "default as your arch\n" \ \ "-f, --fmt (hexline|hexfmt|raw) Output format, default as hexline\n" \ \ "-p, --pid system_process_id Extract bpf filters from process and " \ "print with bpf text form; CAP_SYS_ADMIN is needed to work\n" \ \ "-o, --output file Print to file to avoid mixing " \ "ceccomp output and tracee program output, default as stderr\n" \ \ "-q, --quiet Print emulate result only " \ "or suppress the process info message in trace and probe\n" \ \ "-s, --seize Follow process to trace load-filter " \ "operation\n" \ \ "-c, --color (auto|always|never) When to print in color, default as " \ "auto\n" \ \ "syscall_nr System call number or name (e.g. " \ "0|read)\n" \ "args[0-5], ip args and ip (instruction pointer) " \ "used for emulation, default as 0\n" \ "raw, text File with BPF RAW or BPF TEXT, see " \ "docs for detail, default as stdin\n") #endif ceccomp-4.0/include/i18n.h000066400000000000000000000003541514205130000153320ustar00rootroot00000000000000#ifndef CONFIG_H #include "config.h" #endif #ifndef I18N_H #define I18N_H #include extern locale_t lc_c; #ifdef LOCALEDIR #include #define _(string) gettext (string) #else #define _(string) string #endif #endif ceccomp-4.0/include/lexical/000077500000000000000000000000001514205130000160215ustar00rootroot00000000000000ceccomp-4.0/include/lexical/parser.h000066400000000000000000000037531514205130000174760ustar00rootroot00000000000000#ifndef PARSER_H #define PARSER_H #include "main.h" #include "token.h" #include "utils/hash.h" #include "utils/vector.h" #include #include typedef struct { hkey_t key; uint16_t code_nr; // when key.string != NULL, key stores the label string } label_t; typedef struct { token_type type; uint32_t data; string_t literal; // store idx for MEM | ATTR_LOWARG | ATTR_HIGHARG // store value for NUMBER | TRAP | TRACE | ERRNO } obj_t; typedef struct { obj_t left_var; token_type operator; obj_t right_var; // if operator is NEGATIVE, then it should be A EQUAL NEGATIVE A // but EQUAL is skipped } assign_line_t; typedef struct { // token_t if; // jump_line_t must starts with if, so skip it bool if_bang; // if match '!' before jump_condition bool if_condition; // does condition exists // if true, jt and jf both uint16_t // else jt is uint32_t, jf is ignored // var A; // jump always compare A with something else, so skip it token_type comparator; obj_t cmpobj; label_t jt; label_t jf; // pc += (jump_condition ? jt : jf) + 1 // pc += jt + 1 } jump_line_t; typedef struct { // token_type return // return_line_t must have return, so skip it obj_t ret_obj; } return_line_t; typedef void *empty_line_t; typedef void *eof_line_t; typedef struct { char *error_start; const char *error_msg; } error_line_t; typedef enum { ASSIGN_LINE, JUMP_LINE, RETURN_LINE, EMPTY_LINE, EOF_LINE, ERROR_LINE, } expr_type; typedef struct { expr_type type; string_t label_decl; uint16_t text_nr; uint16_t code_nr; char *line_start; int16_t comment; uint16_t line_len; union { assign_line_t assign_line; jump_line_t jump_line; return_line_t return_line; empty_line_t empty_line; eof_line_t eof_line; error_line_t error_line; }; } statement_t; extern void init_parser (uint32_t scmp_arch); // see vector.h for vector details extern void parser (vector_t *text_v, vector_t *code_ptr_v); #endif ceccomp-4.0/include/lexical/scanner.h000066400000000000000000000005021514205130000176200ustar00rootroot00000000000000#ifndef SCANNER_H #define SCANNER_H #include "token.h" #include struct scanner_t { char *token_start; char *current_char; uint16_t line_nr; }; typedef struct scanner_t scanner_t; typedef struct token_t token_t; extern void init_scanner (char *start); extern void scan_token (token_t *token); #endif ceccomp-4.0/include/lexical/token.h000066400000000000000000000030771514205130000173210ustar00rootroot00000000000000#ifndef TOKEN_H #define TOKEN_H #include "main.h" #include #include // clang-format off // many thing rely on the enum order, careful when modify this typedef enum { ARCH_X86, ARCH_I686, ARCH_X86_64, ARCH_X32, ARCH_ARM, ARCH_AARCH64, ARCH_LOONGARCH64, ARCH_M68K, ARCH_MIPSEL64N32, ARCH_MIPSEL64, ARCH_MIPSEL, ARCH_MIPS64N32, ARCH_MIPS64, ARCH_MIPS, ARCH_PARISC64, ARCH_PARISC, ARCH_PPC64LE, ARCH_PPC64, ARCH_PPC, ARCH_S390X, ARCH_S390, ARCH_RISCV64, KILL_PROC, KILL, ALLOW, NOTIFY, LOG, TRACE, TRAP, ERRNO, RETURN, IF, GOTO, ELSE, COMMA, A, X, MEM, ATTR_LEN, ATTR_SYSCALL, ATTR_ARCH, ATTR_LOWPC, ATTR_HIGHPC, ATTR_LOWARG, ATTR_HIGHARG, DOT, LEFT_BRACKET, RIGHT_BRACKET, LEFT_PAREN, RIGHT_PAREN, ADD_TO, SUB_TO, MULTI_TO, DIVIDE_TO, LSH_TO, RSH_TO, AND_TO, OR_TO, XOR_TO, EQUAL_EQUAL, BANG_EQUAL, GREATER_EQUAL, GREATER_THAN, LESS_EQUAL, LESS_THAN, AND, EQUAL, NEGATIVE, BANG, NUMBER, IDENTIFIER, UNKNOWN, COMMENT, EOL, TOKEN_EOF, LABEL_DECL, OVERFLOW_NUMBER, // IDENTIFIER includes SYSCALL, LABEL // LABEL_DECL = (IDENTIFIER + ':') } token_type; // clang-format on struct token_t { token_type type; char *token_start; uint16_t token_len; uint16_t line_nr; uint32_t data; // data is for NUMBER }; extern const string_t token_pairs[]; typedef struct scanner_t scanner_t; typedef struct token_t token_t; void init_token (token_t *token, scanner_t *scanner, token_type type); void init_token_data (token_t *token, scanner_t *scanner, token_type type, uint32_t data); #endif ceccomp-4.0/include/lib/000077500000000000000000000000001514205130000151465ustar00rootroot00000000000000ceccomp-4.0/include/lib/a5hash-5.25.h000066400000000000000000000501201514205130000170550ustar00rootroot00000000000000/** * @file a5hash.h * * @version 5.25 * * @brief The header file for the "a5hash" 64-bit hash function, "a5hash32" * 32-bit hash function, "a5hash128" 128-bit hash function, and "a5rand" * 64-bit PRNG. * * The source code is written in ISO C99, with full C++ compliance enabled * conditionally and automatically when compiled with a C++ compiler. * * Description is available at https://github.com/avaneev/a5hash * * Email: aleksey.vaneev@gmail.com or info@voxengo.com * * LICENSE: * * Copyright (c) 2025 Aleksey Vaneev * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef A5HASH_INCLUDED #define A5HASH_INCLUDED #define A5HASH_VER_STR "5.25" ///< A5HASH source code version string. /** * @def A5HASH_NS_CUSTOM * @brief If this macro is defined externally, all symbols will be placed into * the C++ namespace specified by this macro and will not be exported to the * global namespace. WARNING: if the macro's value is empty, the symbols will * be placed into the global namespace anyway. */ /** * @def A5HASH_U64_C( x ) * @brief Macro that defines a numeric value as unsigned 64-bit value. * * @param x Value. */ /** * @def A5HASH_NOEX * @brief Macro that defines the "noexcept" function specifier for C++ * environment. */ /** * @def A5HASH_NULL * @brief Macro that defines "nullptr" value, for C++ guidelines compliance. */ /** * @def A5HASH_NS * @brief Macro that defines the actual implementation namespace in C++ * environment, with export of relevant symbols to the global namespace * (if @ref A5HASH_NS_CUSTOM is undefined). */ #if defined( __cplusplus ) #include #if __cplusplus >= 201103L #include #define A5HASH_U64_C( x ) UINT64_C( x ) #define A5HASH_NOEX noexcept #define A5HASH_NULL nullptr #else // __cplusplus >= 201103L #include #define A5HASH_U64_C( x ) (uint64_t) x #define A5HASH_NOEX throw() #define A5HASH_NULL NULL #endif // __cplusplus >= 201103L #if defined( A5HASH_NS_CUSTOM ) #define A5HASH_NS A5HASH_NS_CUSTOM #else // defined( A5HASH_NS_CUSTOM ) #define A5HASH_NS a5hash_impl #endif // defined( A5HASH_NS_CUSTOM ) #else // defined( __cplusplus ) #include #include #define A5HASH_U64_C( x ) (uint64_t) x #define A5HASH_NOEX #define A5HASH_NULL NULL #endif // defined( __cplusplus ) #if defined( _MSC_VER ) #include #endif // defined( _MSC_VER ) #define A5HASH_VAL10 A5HASH_U64_C( 0xAAAAAAAAAAAAAAAA ) ///< `10` bit-pairs. #define A5HASH_VAL01 A5HASH_U64_C( 0x5555555555555555 ) ///< `01` bit-pairs. /** * @def A5HASH_ICC_GCC * @brief Macro that denotes the use of ICC classic compiler with GCC-style * built-in functions. */ #if defined( __INTEL_COMPILER ) && __INTEL_COMPILER >= 1300 && \ !defined( _MSC_VER ) #define A5HASH_ICC_GCC #endif // ICC check /** * @def A5HASH_GCC_BUILTINS * @brief Macro that denotes availability of GCC-style built-in functions. */ #if defined( __GNUC__ ) || defined( __clang__ ) || \ defined( __IBMC__ ) || defined( __IBMCPP__ ) || defined( A5HASH_ICC_GCC ) #define A5HASH_GCC_BUILTINS #endif // GCC built-ins check /** * @def A5HASH_BMI2 * @brief Macro that denotes availability of the `mulx` intrinsic * (MSVC-compatible compilers only). */ #if defined( _MSC_VER ) #if defined( __BMI2__ ) || ( !defined( A5HASH_GCC_BUILTINS ) && \ defined( _M_AMD64 ) && defined( __AVX2__ ) && \ ( defined( __INTEL_COMPILER ) || _MSC_VER >= 1900 )) #include #define A5HASH_BMI2 #else // BMI2 #include #endif // BMI2 #endif // defined( _MSC_VER ) /** * @def A5HASH_STATIC * @brief Macro that defines a function as "static". */ #if ( defined( __cplusplus ) && __cplusplus >= 201703L ) || \ ( defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 202311L ) #define A5HASH_STATIC [[maybe_unused]] static #elif defined( A5HASH_GCC_BUILTINS ) #define A5HASH_STATIC static __attribute__((unused)) #else // defined( A5HASH_GCC_BUILTINS ) #define A5HASH_STATIC static #endif // defined( A5HASH_GCC_BUILTINS ) /** * @def A5HASH_INLINE * @brief Macro that defines a function as inlinable at the compiler's * discretion. */ #define A5HASH_INLINE A5HASH_STATIC inline /** * @def A5HASH_INLINE_F * @brief Macro to force code inlining. */ #if defined( __LP64__ ) || defined( _LP64 ) || \ !( SIZE_MAX <= 0xFFFFFFFFU ) || ( defined( UINTPTR_MAX ) && \ !( UINTPTR_MAX <= 0xFFFFFFFFU )) || defined( __x86_64__ ) || \ defined( __aarch64__ ) || defined( _M_AMD64 ) || defined( _M_ARM64 ) #if defined( A5HASH_GCC_BUILTINS ) #define A5HASH_INLINE_F A5HASH_INLINE __attribute__((always_inline)) #elif defined( _MSC_VER ) #define A5HASH_INLINE_F A5HASH_STATIC __forceinline #endif // defined( _MSC_VER ) #endif // 64-bit platform check #if !defined( A5HASH_INLINE_F ) #define A5HASH_INLINE_F A5HASH_INLINE #endif // !defined( A5HASH_INLINE_F ) #if defined( A5HASH_NS ) namespace A5HASH_NS { using std :: memcpy; using std :: size_t; #if __cplusplus >= 201103L using std :: uint32_t; using std :: uint64_t; using uint8_t = unsigned char; ///< For C++ type aliasing compliance. #endif // __cplusplus >= 201103L #endif // defined( A5HASH_NS ) /** * @{ * @brief Load unsigned value of the specific bit size from memory. * * @param p Load address. */ A5HASH_INLINE_F uint32_t a5hash_lu32( const uint8_t* const p ) A5HASH_NOEX { uint32_t v; memcpy( &v, p, 4 ); return( v ); } A5HASH_INLINE_F uint64_t a5hash_lu64( const uint8_t* const p ) A5HASH_NOEX { uint64_t v; memcpy( &v, p, 8 ); return( v ); } /** @} */ /** * @brief 64-bit by 64-bit unsigned multiplication producing a 128-bit result. * * @param u Multiplier 1. * @param v Multiplier 2. * @param[out] rl The lower half of the 128-bit result. * @param[out] rh The upper half of the 128-bit result. */ A5HASH_INLINE_F void a5hash_umul128( const uint64_t u, const uint64_t v, uint64_t* const rl, uint64_t* const rh ) A5HASH_NOEX { #if defined( A5HASH_BMI2 ) *rl = _mulx_u64( u, v, rh ); #elif defined( _MSC_VER ) && ( defined( _M_ARM64 ) || defined( _M_ARM64EC ) || \ ( defined( __INTEL_COMPILER ) && defined( _M_AMD64 ))) *rl = u * v; *rh = __umulh( u, v ); #elif defined( _MSC_VER ) && ( defined( _M_AMD64 ) || defined( _M_IA64 )) *rl = _umul128( u, v, rh ); #elif defined( __SIZEOF_INT128__ ) || \ ( defined( A5HASH_ICC_GCC ) && defined( __x86_64__ )) __uint128_t r = u; r *= v; *rl = (uint64_t) r; *rh = (uint64_t) ( r >> 64 ); #elif ( defined( __IBMC__ ) || defined( __IBMCPP__ )) && defined( __LP64__ ) *rl = u * v; *rh = __mulhdu( u, v ); #else // defined( __IBMC__ ) // _umul128() code for 32-bit systems, adapted from Hacker's Delight, // Henry S. Warren, Jr. *rl = u * v; const uint32_t u0 = (uint32_t) u; const uint32_t v0 = (uint32_t) v; const uint64_t w0 = (uint64_t) u0 * v0; const uint32_t u1 = (uint32_t) ( u >> 32 ); const uint32_t v1 = (uint32_t) ( v >> 32 ); const uint64_t t = (uint64_t) u1 * v0 + (uint32_t) ( w0 >> 32 ); const uint64_t w1 = (uint64_t) u0 * v1 + (uint32_t) t; *rh = (uint64_t) u1 * v1 + (uint32_t) ( w1 >> 32 ) + (uint32_t) ( t >> 32 ); #endif // defined( __IBMC__ ) } /** * @brief A5HASH 64-bit hash function. * * Produces and returns a 64-bit hash value (digest) of the specified message, * string, or binary data block. Designed for string/small key data hash-map * and hash-table uses. * * @param Msg0 The message to produce a hash from. The alignment of this * pointer is unimportant. It is valid to pass 0 when `MsgLen` equals 0. * @param MsgLen Message length, in bytes, can be zero. * @param UseSeed An optional value to use instead of the default seed (0). * This value can have any number of significant bits and any statistical * quality. * @return 64-bit hash of the input data. */ A5HASH_INLINE_F uint64_t a5hash( const void* const Msg0, size_t MsgLen, const uint64_t UseSeed ) A5HASH_NOEX { const uint8_t* Msg = (const uint8_t*) Msg0; uint64_t val01 = A5HASH_VAL01; uint64_t val10 = A5HASH_VAL10; // The seeds are initialized to mantissa bits of PI. uint64_t Seed1 = A5HASH_U64_C( 0x243F6A8885A308D3 ) ^ MsgLen; uint64_t Seed2 = A5HASH_U64_C( 0x452821E638D01377 ) ^ MsgLen; a5hash_umul128( Seed2 ^ ( UseSeed & val10 ), Seed1 ^ ( UseSeed & val01 ), &Seed1, &Seed2 ); if( MsgLen > 16 ) { val01 ^= Seed1; val10 ^= Seed2; do { a5hash_umul128( (uint64_t) a5hash_lu32( Msg ) << 32 ^ a5hash_lu32( Msg + 4 ) ^ Seed1, (uint64_t) a5hash_lu32( Msg + 8 ) << 32 ^ a5hash_lu32( Msg + 12 ) ^ Seed2, &Seed1, &Seed2 ); MsgLen -= 16; Msg += 16; Seed1 += val01; Seed2 += val10; } while( MsgLen > 16 ); } if( MsgLen == 0 ) { goto _fin; } if( MsgLen > 3 ) { const uint8_t* Msg4; size_t mo; Msg4 = Msg + MsgLen - 4; mo = MsgLen >> 3; Seed1 ^= (uint64_t) a5hash_lu32( Msg ) << 32 | a5hash_lu32( Msg4 ); Seed2 ^= (uint64_t) a5hash_lu32( Msg + mo * 4 ) << 32 | a5hash_lu32( Msg4 - mo * 4 ); _fin: a5hash_umul128( Seed1, Seed2, &Seed1, &Seed2 ); a5hash_umul128( val01 ^ Seed1, Seed2, &Seed1, &Seed2 ); return( Seed1 ^ Seed2 ); } else { Seed1 ^= Msg[ 0 ]; if( --MsgLen != 0 ) { Seed1 ^= (uint64_t) Msg[ 1 ] << 8; if( --MsgLen != 0 ) { Seed1 ^= (uint64_t) Msg[ 2 ] << 16; } } goto _fin; } } /** * @brief 32-bit by 32-bit unsigned multiplication producing a 64-bit result. * * @param u Multiplier 1. * @param v Multiplier 2. * @param[out] rl The lower half of the 64-bit result. * @param[out] rh The upper half of the 64-bit result. */ A5HASH_INLINE_F void a5hash_umul64( const uint32_t u, const uint32_t v, uint32_t* const rl, uint32_t* const rh ) A5HASH_NOEX { const uint64_t r = (uint64_t) u * v; *rl = (uint32_t) r; *rh = (uint32_t) ( r >> 32 ); } /** * @brief A5HASH 32-bit hash function. * * Produces and returns a 32-bit hash value (digest) of the specified message, * string, or binary data block. Designed for string/small key data hash-map * and hash-table uses. * * This function works natively on 32-bit platforms and avoids the performance * penalty of 64-bit arithmetic. * * @param Msg0 The message to produce a hash from. The alignment of this * pointer is unimportant. It is valid to pass 0 when `MsgLen` equals 0. * @param MsgLen Message length, in bytes, can be zero. * @param UseSeed An optional value to use instead of the default seed (0). * This value can have any number of significant bits and any statistical * quality. * @return 32-bit hash of the input data. */ A5HASH_INLINE_F uint32_t a5hash32( const void* const Msg0, size_t MsgLen, const uint32_t UseSeed ) A5HASH_NOEX { const uint8_t* Msg = (const uint8_t*) Msg0; uint32_t val01 = (uint32_t) A5HASH_VAL01; uint32_t val10 = (uint32_t) A5HASH_VAL10; // The seeds are initialized to mantissa bits of PI. uint32_t Seed1 = 0x243F6A88 ^ (uint32_t) MsgLen; uint32_t Seed2 = 0x85A308D3 ^ (uint32_t) MsgLen; uint32_t Seed3, Seed4; uint32_t a, b, c, d; #if SIZE_MAX <= 0xFFFFFFFFU Seed3 = 0xFB0BD3EA; Seed4 = 0x0F58FD47; #else // SIZE_MAX <= 0xFFFFFFFFU a5hash_umul64( (uint32_t) ( MsgLen >> 32 ) ^ 0x452821E6, (uint32_t) ( MsgLen >> 32 ) ^ 0x38D01377, &Seed3, &Seed4 ); #endif // SIZE_MAX <= 0xFFFFFFFFU a5hash_umul64( Seed2 ^ ( UseSeed & val10 ), Seed1 ^ ( UseSeed & val01 ), &Seed1, &Seed2 ); if( MsgLen < 17 ) { if( MsgLen > 3 ) { const uint8_t* const Msg4 = Msg + MsgLen - 4; size_t mo; a = a5hash_lu32( Msg ); b = a5hash_lu32( Msg4 ); if( MsgLen < 9 ) { goto _fin; } mo = MsgLen >> 3; c = a5hash_lu32( Msg + mo * 4 ); d = a5hash_lu32( Msg4 - mo * 4 ); } else { a = 0; b = 0; if( MsgLen != 0 ) { a = Msg[ 0 ]; if( MsgLen != 1 ) { a |= (uint32_t) Msg[ 1 ] << 8; if( MsgLen != 2 ) { a |= (uint32_t) Msg[ 2 ] << 16; } } } goto _fin; } } else { val01 ^= Seed1; val10 ^= Seed2; do { const uint32_t s1 = Seed1; const uint32_t s4 = Seed4; a5hash_umul64( a5hash_lu32( Msg ) + Seed1, a5hash_lu32( Msg + 4 ) + Seed2, &Seed1, &Seed2 ); a5hash_umul64( a5hash_lu32( Msg + 8 ) + Seed3, a5hash_lu32( Msg + 12 ) + Seed4, &Seed3, &Seed4 ); MsgLen -= 16; Msg += 16; Seed1 += val01; Seed2 += s4; Seed3 += s1; Seed4 += val10; } while( MsgLen > 16 ); a = a5hash_lu32( Msg + MsgLen - 8 ); b = a5hash_lu32( Msg + MsgLen - 4 ); if( MsgLen < 9 ) { goto _fin; } c = a5hash_lu32( Msg + MsgLen - 16 ); d = a5hash_lu32( Msg + MsgLen - 12 ); } a5hash_umul64( c + Seed3, d + Seed4, &Seed3, &Seed4 ); _fin: Seed1 ^= Seed3; Seed2 ^= Seed4; a5hash_umul64( a + Seed1, b + Seed2, &Seed1, &Seed2 ); a5hash_umul64( val01 ^ Seed1, Seed2, &a, &b ); return( a ^ b ); } /** * @brief A5HASH 128-bit hash function. * * Produces and returns a 128-bit hash value (digest) of the specified * message, string, or binary data block. Designed for string/small key data * hash-map and hash-table uses. * * @param Msg0 The message to produce a hash from. The alignment of this * pointer is unimportant. It is valid to pass 0 when `MsgLen` equals 0. * @param MsgLen Message length, in bytes, can be zero. * @param UseSeed An optional value to use instead of the default seed (0). * This value can have any number of significant bits and any statistical * quality. * @param[out] rh Pointer to 64-bit variable that receives upper 64 bits of * 128-bit hash. The alignment of this pointer is unimportant. Can be 0. * @return Lower 64 bits of 128-bit hash of the input data. */ A5HASH_INLINE uint64_t a5hash128( const void* const Msg0, size_t MsgLen, const uint64_t UseSeed, void* const rh ) A5HASH_NOEX { const uint8_t* Msg = (const uint8_t*) Msg0; uint64_t val01 = A5HASH_VAL01; uint64_t val10 = A5HASH_VAL10; // The seeds are initialized to mantissa bits of PI. uint64_t Seed1 = A5HASH_U64_C( 0x243F6A8885A308D3 ) ^ MsgLen; uint64_t Seed2 = A5HASH_U64_C( 0x452821E638D01377 ) ^ MsgLen; uint64_t Seed3 = A5HASH_U64_C( 0xA4093822299F31D0 ); uint64_t Seed4 = A5HASH_U64_C( 0xC0AC29B7C97C50DD ); uint64_t a, b, c, d; a5hash_umul128( Seed2 ^ ( UseSeed & val10 ), Seed1 ^ ( UseSeed & val01 ), &Seed1, &Seed2 ); if( MsgLen < 17 ) { if( MsgLen > 3 ) { const uint8_t* Msg4; size_t mo; Msg4 = Msg + MsgLen - 4; mo = MsgLen >> 3; a = (uint64_t) a5hash_lu32( Msg ) << 32 | a5hash_lu32( Msg4 ); b = (uint64_t) a5hash_lu32( Msg + mo * 4 ) << 32 | a5hash_lu32( Msg4 - mo * 4 ); _fin16: a5hash_umul128( a + Seed1, b + Seed2, &Seed1, &Seed2 ); a5hash_umul128( val01 ^ Seed1, Seed2, &a, &b ); a ^= b; if( rh != A5HASH_NULL ) { a5hash_umul128( Seed1 ^ Seed3, Seed2 ^ Seed4, &Seed3, &Seed4 ); Seed3 ^= Seed4; memcpy( rh, &Seed3, 8 ); } return( a ); } else { a = 0; b = 0; if( MsgLen != 0 ) { a = Msg[ 0 ]; if( --MsgLen != 0 ) { a |= (uint64_t) Msg[ 1 ] << 8; if( --MsgLen != 0 ) { a |= (uint64_t) Msg[ 2 ] << 16; } } } goto _fin16; } } if( MsgLen < 33 ) { a = (uint64_t) a5hash_lu32( Msg ) << 32 | a5hash_lu32( Msg + 4 ); b = (uint64_t) a5hash_lu32( Msg + 8 ) << 32 | a5hash_lu32( Msg + 12 ); c = (uint64_t) a5hash_lu32( Msg + MsgLen - 16 ) << 32 | a5hash_lu32( Msg + MsgLen - 12 ); d = (uint64_t) a5hash_lu32( Msg + MsgLen - 8 ) << 32 | a5hash_lu32( Msg + MsgLen - 4 ); _fin_m: a5hash_umul128( c + Seed3, d + Seed4, &Seed3, &Seed4 ); _fin: Seed1 ^= Seed3; Seed2 ^= Seed4; a5hash_umul128( a + Seed1, b + Seed2, &Seed1, &Seed2 ); a5hash_umul128( val01 ^ Seed1, Seed2, &a, &b ); a ^= b; if( rh != A5HASH_NULL ) { a5hash_umul128( Seed1 ^ Seed3, Seed2 ^ Seed4, &Seed3, &Seed4 ); Seed3 ^= Seed4; memcpy( rh, &Seed3, 8 ); } return( a ); } else { val01 ^= Seed1; val10 ^= Seed2; if( MsgLen > 64 ) { uint64_t Seed5 = A5HASH_U64_C( 0x082EFA98EC4E6C89 ); uint64_t Seed6 = A5HASH_U64_C( 0x3F84D5B5B5470917 ); uint64_t Seed7 = A5HASH_U64_C( 0x13198A2E03707344 ); uint64_t Seed8 = A5HASH_U64_C( 0xBE5466CF34E90C6C ); do { const uint64_t s1 = Seed1; const uint64_t s3 = Seed3; const uint64_t s5 = Seed5; a5hash_umul128( a5hash_lu64( Msg ) + Seed1, a5hash_lu64( Msg + 32 ) + Seed2, &Seed1, &Seed2 ); Seed1 += val01; Seed2 += Seed8; a5hash_umul128( a5hash_lu64( Msg + 8 ) + Seed3, a5hash_lu64( Msg + 40 ) + Seed4, &Seed3, &Seed4 ); Seed3 += s1; Seed4 += val10; a5hash_umul128( a5hash_lu64( Msg + 16 ) + Seed5, a5hash_lu64( Msg + 48 ) + Seed6, &Seed5, &Seed6 ); a5hash_umul128( a5hash_lu64( Msg + 24 ) + Seed7, a5hash_lu64( Msg + 56 ) + Seed8, &Seed7, &Seed8 ); MsgLen -= 64; Msg += 64; Seed5 += s3; Seed6 += val10; Seed7 += s5; Seed8 += val10; } while( MsgLen > 64 ); Seed1 ^= Seed5; Seed2 ^= Seed6; Seed3 ^= Seed7; Seed4 ^= Seed8; if( MsgLen > 32 ) { goto _tail32; } } else { uint64_t s1; _tail32: s1 = Seed1; a5hash_umul128( a5hash_lu64( Msg ) + Seed1, a5hash_lu64( Msg + 8 ) + Seed2, &Seed1, &Seed2 ); Seed1 += val01; Seed2 += Seed4; a5hash_umul128( a5hash_lu64( Msg + 16 ) + Seed3, a5hash_lu64( Msg + 24 ) + Seed4, &Seed3, &Seed4 ); MsgLen -= 32; Msg += 32; Seed3 += s1; Seed4 += val10; } a = a5hash_lu64( Msg + MsgLen - 16 ); b = a5hash_lu64( Msg + MsgLen - 8 ); if( MsgLen < 17 ) { goto _fin; } c = a5hash_lu64( Msg + MsgLen - 32 ); d = a5hash_lu64( Msg + MsgLen - 24 ); goto _fin_m; } } /** * @brief A5RAND 64-bit pseudo-random number generator. * * A simple, reliable, self-starting, yet efficient PRNG with a 2^64 period. * 0.50 cycles/byte performance. It self-starts in 4 iterations, which is the * suggested "warm-up" period before using its output when seeds are * initialized with an arbitrary value. If initialized with high-quality, * uniformly random value (e.g., from the operating system's entropy or a * hash function's output), the PRNG output is valid from the start. * * @param[in,out] Seed1 Seed value 1. Can be initialized to any value * (even 0). Should not be used as the PRNG value. * @param[in,out] Seed2 Seed value 2. Must be initialized to the same value as * `Seed1`. Should not be used as the PRNG value. * @return The next uniformly random 64-bit value. */ A5HASH_INLINE_F uint64_t a5rand( uint64_t* const Seed1, uint64_t* const Seed2 ) A5HASH_NOEX { uint64_t s1 = *Seed1; uint64_t s2 = *Seed2; a5hash_umul128( s1 + A5HASH_VAL01, s2 + A5HASH_VAL10, &s1, &s2 ); *Seed1 = s1; *Seed2 = s2; return( s1 ^ s2 ); } #if defined( A5HASH_NS ) } // namespace A5HASH_NS #if !defined( A5HASH_NS_CUSTOM ) namespace { using A5HASH_NS :: a5hash_umul128; using A5HASH_NS :: a5hash; using A5HASH_NS :: a5hash32; using A5HASH_NS :: a5hash128; using A5HASH_NS :: a5rand; } // namespace #endif // !defined( A5HASH_NS_CUSTOM ) #endif // defined( A5HASH_NS ) // Defines for Doxygen. #if !defined( A5HASH_NS_CUSTOM ) #define A5HASH_NS_CUSTOM #endif // !defined( A5HASH_NS_CUSTOM ) #undef A5HASH_NS_CUSTOM #undef A5HASH_U64_C #undef A5HASH_NOEX #undef A5HASH_NULL #undef A5HASH_VAL10 #undef A5HASH_VAL01 #undef A5HASH_ICC_GCC #undef A5HASH_GCC_BUILTINS #undef A5HASH_BMI2 #undef A5HASH_STATIC #undef A5HASH_INLINE #undef A5HASH_INLINE_F #endif // A5HASH_INCLUDED ceccomp-4.0/include/lib/verstable-2.2.1.h000066400000000000000000002451311514205130000177520ustar00rootroot00000000000000/*------------------------------------------------- VERSTABLE v2.2.1 --------------------------------------------------- Verstable is a C99-compatible, open-addressing hash table using quadratic probing and the following additions: * All keys that hash (i.e. "belong") to the same bucket (their "home bucket") are linked together by an 11-bit integer specifying the quadratic displacement, relative to that bucket, of the next key in the chain. * If a chain of keys exists for a given bucket, then it always begins at that bucket. To maintain this policy, a 1-bit flag is used to mark whether the key occupying a bucket belongs there. When inserting a new key, if the bucket it belongs to is occupied by a key that does not belong there, then the occupying key is evicted and the new key takes the bucket. * A 4-bit fragment of each key's hash code is also stored. * The aforementioned metadata associated with each bucket (the 4-bit hash fragment, the 1-bit flag, and the 11-bit link to the next key in the chain) are stored together in a uint16_t array rather than in the bucket alongside the key and (optionally) the value. One way to conceptualize this scheme is as a chained hash table in which overflowing keys are stored not in separate memory allocations but in otherwise unused buckets. In this regard, it shares similarities with Malte Skarupke's Bytell hash table (https://www.youtube.com/watch?v=M2fKMP47slQ) and traditional "coalesced hashing". Advantages of this scheme include: * Fast lookups impervious to load factor: If the table contains any key belonging to the lookup key's home bucket, then that bucket contains the first in a traversable chain of all keys belonging to it. Hence, only the home bucket and other buckets containing keys belonging to it are ever probed. Moreover, the stored hash fragments allow skipping most non-matching keys in the chain without accessing the actual buckets array or calling the (potentially expensive) key comparison function. * Fast insertions: Insertions are faster than they are in other schemes that move keys around (e.g. Robin Hood) because they only move, at most, one existing key. * Fast, tombstone-free deletions: Deletions, which usually require tombstones in quadratic-probing hash tables, are tombstone-free and only move, at most, one existing key. * Fast iteration: The separate metadata array allows keys in sparsely populated tables to be found without incurring the frequent cache misses that would result from traversing the buckets array. Usage example: +---------------------------------------------------------+----------------------------------------------------------+ | Using the generic macro API (C11 and later): | Using the prefixed functions API (C99 and later): | +---------------------------------------------------------+----------------------------------------------------------+ | #include | #include | | | | | // Instantiating a set template. | // Instantiating a set template. | | #define NAME int_set | #define NAME int_set | | #define KEY_TY int | #define KEY_TY int | | #include "verstable.h" | #define HASH_FN vt_hash_integer | | | #define CMPR_FN vt_cmpr_integer | | // Instantiating a map template. | #include "verstable.h" | | #define NAME int_int_map | | | #define KEY_TY int | // Instantiating a map template. | | #define VAL_TY int | #define NAME int_int_map | | #include "verstable.h" | #define KEY_TY int | | | #define VAL_TY int | | int main( void ) | #define HASH_FN vt_hash_integer | | { | #define CMPR_FN vt_cmpr_integer | | // Set. | #include "verstable.h" | | | | | int_set our_set; | int main( void ) | | vt_init( &our_set ); | { | | | // Set. | | // Inserting keys. | | | for( int i = 0; i < 10; ++i ) | int_set our_set; | | { | int_set_init( &our_set ); | | int_set_itr itr = vt_insert( &our_set, i ); | | | if( vt_is_end( itr ) ) | // Inserting keys. | | { | for( int i = 0; i < 10; ++i ) | | // Out of memory, so abort. | { | | vt_cleanup( &our_set ); | int_set_itr itr = | | return 1; | int_set_insert( &our_set, i ); | | } | if( int_set_is_end( itr ) ) | | } | { | | | // Out of memory, so abort. | | // Erasing keys. | int_set_cleanup( &our_set ); | | for( int i = 0; i < 10; i += 3 ) | return 1; | | vt_erase( &our_set, i ); | } | | | } | | // Retrieving keys. | | | for( int i = 0; i < 10; ++i ) | // Erasing keys. | | { | for( int i = 0; i < 10; i += 3 ) | | int_set_itr itr = vt_get( &our_set, i ); | int_set_erase( &our_set, i ); | | if( !vt_is_end( itr ) ) | | | printf( "%d ", itr.data->key ); | // Retrieving keys. | | } | for( int i = 0; i < 10; ++i ) | | // Printed: 1 2 4 5 7 8 | { | | | int_set_itr itr = int_set_get( &our_set, i ); | | // Iteration. | if( !int_set_is_end( itr ) ) | | for( | printf( "%d ", itr.data->key ); | | int_set_itr itr = vt_first( &our_set ); | } | | !vt_is_end( itr ); | // Printed: 1 2 4 5 7 8 | | itr = vt_next( itr ) | | | ) | // Iteration. | | printf( "%d ", itr.data->key ); | for( | | // Printed: 2 4 7 1 5 8 | int_set_itr itr = | | | int_set_first( &our_set ); | | vt_cleanup( &our_set ); | !int_set_is_end( itr ); | | | itr = int_set_next( itr ) | | // Map. | ) | | | printf( "%d ", itr.data->key ); | | int_int_map our_map; | // Printed: 2 4 7 1 5 8 | | vt_init( &our_map ); | | | | int_set_cleanup( &our_set ); | | // Inserting keys and values. | | | for( int i = 0; i < 10; ++i ) | // Map. | | { | | | int_int_map_itr itr = | int_int_map our_map; | | vt_insert( &our_map, i, i + 1 ); | int_int_map_init( &our_map ); | | if( vt_is_end( itr ) ) | | | { | // Inserting keys and values. | | // Out of memory, so abort. | for( int i = 0; i < 10; ++i ) | | vt_cleanup( &our_map ); | { | | return 1; | int_int_map_itr itr = | | } | int_int_map_insert( &our_map, i, i + 1 ); | | } | if( int_int_map_is_end( itr ) ) | | | { | | // Erasing keys and values. | // Out of memory, so abort. | | for( int i = 0; i < 10; i += 3 ) | int_int_map_cleanup( &our_map ); | | vt_erase( &our_map, i ); | return 1; | | | } | | // Retrieving keys and values. | } | | for( int i = 0; i < 10; ++i ) | | | { | // Erasing keys and values. | | int_int_map_itr itr = vt_get( &our_map, i ); | for( int i = 0; i < 10; i += 3 ) | | if( !vt_is_end( itr ) ) | int_int_map_erase( &our_map, i ); | | printf( | | | "%d:%d ", | // Retrieving keys and values. | | itr.data->key, | for( int i = 0; i < 10; ++i ) | | itr.data->val | { | | ); | int_int_map_itr itr = | | } | int_int_map_get( &our_map, i ); | | // Printed: 1:2 2:3 4:5 5:6 7:8 8:9 | if( !int_int_map_is_end( itr ) ) | | | printf( | | // Iteration. | "%d:%d ", | | for( | itr.data->key, | | int_int_map_itr itr = vt_first( &our_map ); | itr.data->val | | !vt_is_end( itr ); | ); | | itr = vt_next( itr ) | } | | ) | // Printed: 1:2 2:3 4:5 5:6 7:8 8:9 | | printf( | | | "%d:%d ", | // Iteration. | | itr.data->key, | for( | | itr.data->val | int_int_map_itr itr = | | ); | int_int_map_first( &our_map ); | | // Printed: 2:3 4:5 7:8 1:2 5:6 8:9 | !int_int_map_is_end( itr ); | | | itr = int_int_map_next( itr ) | | vt_cleanup( &our_map ); | ) | | } | printf( | | | "%d:%d ", | | | itr.data->key, | | | itr.data->val | | | ); | | | // Printed: 2:3 4:5 7:8 1:2 5:6 8:9 | | | | | | int_int_map_cleanup( &our_map ); | | | } | | | | +---------------------------------------------------------+----------------------------------------------------------+ API: Instantiating a hash table template: Create a new hash table type in the following manner: #define NAME #define KEY_TY #include "verstable.h" The NAME macro specifies the name of hash table type that the library will declare, the prefix for the functions associated with it, and the prefix for the associated iterator type. The KEY_TY macro specifies the key type. In C99, it is also always necessary to define HASH_FN and CMPR_FN (see below) before including the header. The following macros may also be defined before including the header: #define VAL_TY The type of the value associated with each key. If this macro is defined, the hash table acts as a map associating keys with values. Otherwise, it acts as a set containing only keys. #define HASH_FN The name of the existing function used to hash each key. The function should have the signature uint64_t ( KEY_TY key ) and return a 64-bit hash code. For best performance, the hash function should provide a high level of entropy across all bits. There are two default hash functions: vt_hash_integer for all integer types up to 64 bits in size, and vt_hash_string for NULL-terminated strings (i.e. char *). When KEY_TY is one of such types and the compiler is in C11 mode or later, HASH_FN may be left undefined, in which case the appropriate default function is inferred from KEY_TY. Otherwise, HASH_FN must be defined. #define CMPR_FN The name of the existing function used to compare two keys. The function should have the signature bool ( KEY_TY key_1, KEY_TY key_2 ) and return true if the two keys are equal. There are two default comparison functions: vt_cmpr_integer for all integer types up to 64 bits in size, and vt_cmpr_string for NULL-terminated strings (i.e. char *). As with the default hash functions, in C11 or later the appropriate default comparison function is inferred if KEY_TY is one of such types and CMPR_FN is left undefined. Otherwise, CMPR_FN must be defined. #define MAX_LOAD The floating-point load factor at which the hash table automatically doubles the size of its internal buckets array. The default is 0.9, i.e. 90%. #define KEY_DTOR_FN The name of the existing destructor function, with the signature void ( KEY_TY key ), called on a key when it is erased from the table or replaced by a newly inserted key. The API functions that may call the key destructor are NAME_insert, NAME_erase, NAME_erase_itr, NAME_clear, and NAME_cleanup. #define VAL_DTOR_FN The name of the existing destructor function, with the signature void ( VAL_TY val ), called on a value when it is erased from the table or replaced by a newly inserted value. The API functions that may call the value destructor are NAME_insert, NAME_erase, NAME_erase_itr, NAME_clear, and NAME_cleanup. #define CTX_TY The type of the hash table type's ctx (context) member. This member only exists if CTX_TY was defined. It is intended to be used in conjunction with MALLOC_FN and FREE_FN (see below). #define MALLOC_FN The name of the existing function used to allocate memory. If CTX_TY was defined, the signature should be void *( size_t size, CTX_TY *ctx ), where size is the number of bytes to allocate and ctx points to the table's ctx member. Otherwise, the signature should be void *( size_t size ). The default wraps stdlib.h's malloc. #define FREE_FN The name of the existing function used to free memory. If CTX_TY was defined, the signature should be void ( void *ptr, size_t size, CTX_TY *ctx ), where ptr points to the memory to free, size is the number of bytes that were allocated, and ctx points to the table's ctx member. Otherwise, the signature should be void ( void *ptr, size_t size ). The default wraps stdlib.h's free. #define HEADER_MODE #define IMPLEMENTATION_MODE By default, all hash table functions are defined as static inline functions, the intent being that a given hash table template should be instantiated once per translation unit; for best performance, this is the recommended way to use the library. However, it is also possible to separate the struct definitions and function declarations from the function definitions such that one implementation can be shared across all translation units (as in a traditional header and source file pair). In that case, instantiate a template wherever it is needed by defining HEADER_MODE, along with only NAME, KEY_TY, and (optionally) VAL_TY, CTX_TY, and header guards, and including the library, e.g.: #ifndef INT_INT_MAP_H #define INT_INT_MAP_H #define NAME int_int_map #define KEY_TY int #define VAL_TY int #define HEADER_MODE #include "verstable.h" #endif In one source file, define IMPLEMENTATION_MODE, along with NAME, KEY_TY, and any of the aforementioned optional macros, and include the library, e.g.: #define NAME int_int_map #define KEY_TY int #define VAL_TY int #define HASH_FN vt_hash_integer // C99. #define CMPR_FN vt_cmpr_integer // C99. #define MAX_LOAD 0.8 #define IMPLEMENTATION_MODE #include "verstable.h" Including the library automatically undefines all the aforementioned macros after they have been used to instantiate the template. Functions: The functions associated with a hash table type are all prefixed with the name the user supplied via the NAME macro. In C11 and later, the generic "vt_"-prefixed macros may be used to automatically select the correct version of the specified function based on the arguments. void NAME_init( NAME *table ) void NAME_init( NAME *table, CTX_TY ctx ) // C11 generic macro: vt_init. Initializes the table for use. If CTX_TY was defined, ctx sets the table's ctx member. bool NAME_init_clone( NAME *table, NAME *source ) bool NAME_init_clone( NAME *table, NAME *source, CTX_TY ctx ) // C11 generic macro: vt_init_clone. Initializes the table as a shallow copy of the specified source table. If CTX_TY was defined, ctx sets the table's ctx member. Returns false in the case of memory allocation failure. size_t NAME_size( NAME *table ) // C11 generic macro: vt_size. Returns the number of keys currently in the table. size_t NAME_bucket_count( NAME *table ) // C11 generic macro: vt_bucket_count. Returns the table's current bucket count. NAME_itr NAME_insert( NAME *table, KEY_TY key ) NAME_itr NAME_insert( NAME *table, KEY_TY key, VAL_TY val ) // C11 generic macro: vt_insert. Inserts the specified key (and value, if VAL_TY was defined) into the hash table. If the same key already exists, then the new key (and value) replaces the existing key (and value). Returns an iterator to the new key, or an end iterator in the case of memory allocation failure. NAME_itr NAME_get_or_insert( NAME *table, KEY_TY key ) NAME_itr NAME_get_or_insert( NAME *table, KEY_TY key, VAL_TY val ) // C11 generic macro: vt_get_or_insert. Inserts the specified key (and value, if VAL_TY was defined) if it does not already exist in the table. Returns an iterator to the new key if it was inserted, or an iterator to the existing key, or an end iterator if the key did not exist but the new key could not be inserted because of memory allocation failure. Determine whether the key was inserted by comparing the table's size before and after the call. NAME_itr NAME_get( NAME *table, KEY_TY key ) // C11 generic macro: vt_get. Returns a iterator to the specified key, or an end iterator if no such key exists. bool NAME_erase( NAME *table, KEY_TY key ) // C11 generic macro: vt_erase. Erases the specified key (and associated value, if VAL_TY was defined), if it exists. Returns true if a key was erased. NAME_itr NAME_erase_itr( NAME *table, NAME_itr itr ) // C11 generic macro: vt_erase_itr. Erases the key (and associated value, if VAL_TY was defined) pointed to by the specified iterator. Returns an iterator to the next key in the table, or an end iterator if the erased key was the last one. bool NAME_reserve( NAME *table, size_t size ) // C11 generic macro: vt_reserve. Ensures that the bucket count is large enough to support the specified key count (i.e. size) without rehashing. Returns false if unsuccessful due to memory allocation failure. bool NAME_shrink( NAME *table ) // C11 generic macro: vt_shrink. Shrinks the bucket count to best accommodate the current size. Returns false if unsuccessful due to memory allocation failure. NAME_itr NAME_first( NAME *table ) // C11 generic macro: vt_first. Returns an iterator to the first key in the table, or an end iterator if the table is empty. bool NAME_is_end( NAME_itr itr ) // C11 generic macro: vt_is_end. Returns true if the iterator is an end iterator. NAME_itr NAME_next( NAME_itr itr ) // C11 generic macro: vt_next. Returns an iterator to the key after the one pointed to by the specified iterator, or an end iterator if the specified iterator points to the last key in the table. void NAME_clear( NAME *table ) // C11 generic macro: vt_clear. Erases all keys (and values, if VAL_TY was defined) in the table. void NAME_cleanup( NAME *table ) // C11 generic macro: vt_cleanup. Erases all keys (and values, if VAL_TY was defined) in the table, frees all memory associated with it, and initializes it for reuse. Iterators: Access the key (and value, if VAL_TY was defined) that an iterator points to using the NAME_itr struct's data member: itr.data->key itr.data->val Functions that may insert new keys (NAME_insert and NAME_get_or_insert), erase keys (NAME_erase and NAME_erase_itr), or reallocate the internal bucket array (NAME_reserve and NAME_shrink) invalidate all existing iterators. To delete keys during iteration and resume iterating, use the return value of NAME_erase_itr. Version history: 06/05/2025 2.2.1: Fixed incorrect signature of NAME_is_end in documentation. 18/04/2025 2.2.0: Added const qualifier to the table parameter of NAME_size, NAME_bucket_count, NAME_get, and NAME_first and to the source parameter of NAME_init_clone. Added default support for const char * strings. Added support for -Wextra. Replaced FNV-1a with Wyhash as the default string hash function. 18/06/2024 2.1.1: Fixed a bug affecting iteration on big-endian platforms under MSVC. 27/05/2024 2.1.0: Replaced the Murmur3 mixer with the fast-hash mixer as the default integer hash function. Fixed a bug that could theoretically cause a crash on rehash (triggerable in testing using NAME_shrink with a maximum load factor significantly higher than 1.0). 06/02/2024 2.0.0: Improved custom allocator support by introducing the CTX_TY option and allowing user-supplied free functions to receive the allocation size. Improved documentation. Introduced various optimizations, including storing the buckets-array size mask instead of the bucket count, eliminating empty-table checks, combining the buckets memory and metadata memory into one allocation, and adding branch prediction macros. Fixed a bug that caused a key to be used after destruction during erasure. 12/12/2023 1.0.0: Initial release. License (MIT): Copyright (c) 2023-2025 Jackson L. Allan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*--------------------------------------------------------------------------------------------------------------------*/ /* Common header section */ /*--------------------------------------------------------------------------------------------------------------------*/ #ifndef VERSTABLE_H #define VERSTABLE_H #include #include #include #include #include #include // Two-way concatenation macro. #define VT_CAT_( a, b ) a##b #define VT_CAT( a, b ) VT_CAT_( a, b ) // Branch optimization macros. #ifdef __GNUC__ #define VT_LIKELY( expression ) __builtin_expect( (bool)( expression ), true ) #define VT_UNLIKELY( expression ) __builtin_expect( (bool)( expression ), false ) #else #define VT_LIKELY( expression ) ( expression ) #define VT_UNLIKELY( expression ) ( expression ) #endif // Masks for manipulating and extracting data from a bucket's uint16_t metadatum. #define VT_EMPTY 0x0000 #define VT_HASH_FRAG_MASK 0xF000 // 0b1111000000000000. #define VT_IN_HOME_BUCKET_MASK 0x0800 // 0b0000100000000000. #define VT_DISPLACEMENT_MASK 0x07FF // 0b0000011111111111, also denotes the displacement limit. Set to VT_LOAD to 1.0 // to test proper handling of encroachment on the displacement limit during // inserts. // Extracts a hash fragment from a uint64_t hash code. // We take the highest four bits so that keys that map (via modulo) to the same bucket have distinct hash fragments. static inline uint16_t vt_hashfrag( uint64_t hash ) { return ( hash >> 48 ) & VT_HASH_FRAG_MASK; } // Standard quadratic probing formula that guarantees that all buckets are visited when the bucket count is a power of // two (at least in theory, because the displacement limit could terminate the search early when the bucket count is // high). static inline size_t vt_quadratic( uint16_t displacement ) { return ( (size_t)displacement * displacement + displacement ) / 2; } #define VT_MIN_NONZERO_BUCKET_COUNT 8 // Must be a power of two. // Function to find the left-most non-zero uint16_t in a uint64_t. // This function is used when we scan four buckets at a time while iterating and relies on compiler intrinsics wherever // possible. #if defined( __GNUC__ ) && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF static inline int vt_first_nonzero_uint16( uint64_t val ) { const uint16_t endian_checker = 0x0001; if( *(const char *)&endian_checker ) // Little-endian (the compiler will optimize away the check at -O1 and above). return __builtin_ctzll( val ) / 16; return __builtin_clzll( val ) / 16; } #elif defined( _MSC_VER ) && ( defined( _M_X64 ) || defined( _M_ARM64 ) ) #include #pragma intrinsic(_BitScanForward64) #pragma intrinsic(_BitScanReverse64) static inline int vt_first_nonzero_uint16( uint64_t val ) { unsigned long result; const uint16_t endian_checker = 0x0001; if( *(const char *)&endian_checker ) _BitScanForward64( &result, val ); else { _BitScanReverse64( &result, val ); result = 63 - result; } return result / 16; } #else static inline int vt_first_nonzero_uint16( uint64_t val ) { int result = 0; uint32_t half; memcpy( &half, &val, sizeof( uint32_t ) ); if( !half ) result += 2; uint16_t quarter; memcpy( &quarter, (char *)&val + result * sizeof( uint16_t ), sizeof( uint16_t ) ); if( !quarter ) result += 1; return result; } #endif // When the bucket count is zero, setting the metadata pointer to point to a VT_EMPTY placeholder, rather than NULL, // allows us to avoid checking for a zero bucket count during insertion and lookup. static const uint16_t vt_empty_placeholder_metadatum = VT_EMPTY; // Default hash and comparison functions. // Fast-hash, as described by https://jonkagstrom.com/bit-mixer-construction and // https://code.google.com/archive/p/fast-hash. // In testing, this hash function provided slightly better performance than the Murmur3 mixer. static inline uint64_t vt_hash_integer( uint64_t key ) { key ^= key >> 23; key *= 0x2127599bf4325c37ull; key ^= key >> 47; return key; } // For hashing strings, we use the public-domain Wyhash // (https://github.com/wangyi-fudan/wyhash) with the following modifications: // * We use a fixed seed and secret (the defaults suggested in the Wyhash repository). // * We do not handle endianness, so the result will differ depending on the platform. // * We omit the code optimized for 32-bit platforms. static inline void vt_wymum( uint64_t *a, uint64_t *b ) { #if defined( __SIZEOF_INT128__ ) __uint128_t r = *a; r *= *b; *a = (uint64_t)r; *b = (uint64_t)( r >> 64 ); #elif defined( _MSC_VER ) && defined( _M_X64 ) *a = _umul128( *a, *b, b ); #else uint64_t ha = *a >> 32; uint64_t hb = *b >> 32; uint64_t la = (uint32_t)*a; uint64_t lb = (uint32_t)*b; uint64_t rh = ha * hb; uint64_t rm0 = ha * lb; uint64_t rm1 = hb * la; uint64_t rl = la * lb; uint64_t t = rl + ( rm0 << 32 ); uint64_t c = t < rl; uint64_t lo = t + ( rm1 << 32 ); c += lo < t; uint64_t hi = rh + ( rm0 >> 32 ) + ( rm1 >> 32 ) + c; *a = lo; *b = hi; #endif } static inline uint64_t vt_wymix( uint64_t a, uint64_t b ) { vt_wymum( &a, &b ); return a ^ b; } static inline uint64_t vt_wyr8( const unsigned char *p ) { uint64_t v; memcpy( &v, p, 8 ); return v; } static inline uint64_t vt_wyr4( const unsigned char *p ) { uint32_t v; memcpy( &v, p, 4 ); return v; } static inline uint64_t vt_wyr3( const unsigned char *p, size_t k ) { return ( ( (uint64_t)p[ 0 ] ) << 16 ) | ( ( (uint64_t)p[ k >> 1 ] ) << 8 ) | p[ k - 1 ]; } static inline size_t vt_wyhash( const void *key, size_t len ) { const unsigned char *p = (const unsigned char *)key; uint64_t seed = 0xca813bf4c7abf0a9ull; uint64_t a; uint64_t b; if( VT_LIKELY( len <= 16 ) ) { if( VT_LIKELY( len >= 4 ) ) { a = ( vt_wyr4( p ) << 32 ) | vt_wyr4( p + ( ( len >> 3 ) << 2 ) ); b = ( vt_wyr4( p + len - 4 ) << 32 ) | vt_wyr4( p + len - 4 - ( ( len >> 3 ) << 2 ) ); } else if( VT_LIKELY( len > 0 ) ) { a = vt_wyr3( p, len ); b = 0; } else { a = 0; b = 0; } } else { size_t i = len; if( VT_UNLIKELY( i >= 48 ) ) { uint64_t see1 = seed; uint64_t see2 = seed; do{ seed = vt_wymix( vt_wyr8( p ) ^ 0x8bb84b93962eacc9ull, vt_wyr8( p + 8 ) ^ seed ); see1 = vt_wymix( vt_wyr8( p + 16 ) ^ 0x4b33a62ed433d4a3ull, vt_wyr8( p + 24 ) ^ see1 ); see2 = vt_wymix( vt_wyr8( p + 32 ) ^ 0x4d5a2da51de1aa47ull, vt_wyr8( p + 40 ) ^ see2 ); p += 48; i -= 48; } while( VT_LIKELY( i >= 48 ) ); seed ^= see1 ^ see2; } while( VT_UNLIKELY( i > 16 ) ) { seed = vt_wymix( vt_wyr8( p ) ^ 0x8bb84b93962eacc9ull, vt_wyr8( p + 8 ) ^ seed ); i -= 16; p += 16; } a = vt_wyr8( p + i - 16 ); b = vt_wyr8( p + i - 8 ); } a ^= 0x8bb84b93962eacc9ull; b ^= seed; vt_wymum( &a, &b ); return (size_t)vt_wymix( a ^ 0x2d358dccaa6c78a5ull ^ len, b ^ 0x8bb84b93962eacc9ull ); } static inline uint64_t vt_hash_string( const char *key ) { return vt_wyhash( key, strlen( key ) ); } static inline bool vt_cmpr_integer( uint64_t key_1, uint64_t key_2 ) { return key_1 == key_2; } static inline bool vt_cmpr_string( const char *key_1, const char *key_2 ) { return strcmp( key_1, key_2 ) == 0; } // Default allocation and free functions. static inline void *vt_malloc( size_t size ) { return malloc( size ); } static inline void vt_free( void *ptr, size_t size ) { (void)size; free( ptr ); } static inline void *vt_malloc_with_ctx( size_t size, void *ctx ) { (void)ctx; return malloc( size ); } static inline void vt_free_with_ctx( void *ptr, size_t size, void *ctx ) { (void)size; (void)ctx; free( ptr ); } // The rest of the common header section pertains to the C11 generic macro API. // This interface is based on the extendible-_Generic mechanism documented in detail at // https://github.com/JacksonAllan/CC/blob/main/articles/Better_C_Generics_Part_1_The_Extendible_Generic.md. // In summary, instantiating a template also defines wrappers for the template's types and functions with names in the // pattern of vt_table_NNNN and vt_init_NNNN, where NNNN is an automatically generated integer unique to the template // instance in the current translation unit. // These wrappers plug into _Generic-based API macros, which use preprocessor magic to automatically generate _Generic // slots for every existing template instance. #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined( VT_NO_C11_GENERIC_API ) // Octal counter that supports up to 511 hash table templates. #define VT_TEMPLATE_COUNT_D1 0 // Digit 1, i.e. least significant digit. #define VT_TEMPLATE_COUNT_D2 0 #define VT_TEMPLATE_COUNT_D3 0 // Four-way concatenation macro. #define VT_CAT_4_( a, b, c, d ) a##b##c##d #define VT_CAT_4( a, b, c, d ) VT_CAT_4_( a, b, c, d ) // Provides the current value of the counter as a three-digit octal number preceded by 0. #define VT_TEMPLATE_COUNT VT_CAT_4( 0, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2, VT_TEMPLATE_COUNT_D1 ) // _Generic-slot generation macros. #define VT_GENERIC_SLOT( ty, fn, n ) , VT_CAT( ty, n ): VT_CAT( fn, n ) #define VT_R1_0( ty, fn, d3, d2 ) #define VT_R1_1( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 0 ) ) #define VT_R1_2( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 1 ) ) VT_R1_1( ty, fn, d3, d2 ) #define VT_R1_3( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 2 ) ) VT_R1_2( ty, fn, d3, d2 ) #define VT_R1_4( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 3 ) ) VT_R1_3( ty, fn, d3, d2 ) #define VT_R1_5( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 4 ) ) VT_R1_4( ty, fn, d3, d2 ) #define VT_R1_6( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 5 ) ) VT_R1_5( ty, fn, d3, d2 ) #define VT_R1_7( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 6 ) ) VT_R1_6( ty, fn, d3, d2 ) #define VT_R1_8( ty, fn, d3, d2 ) VT_GENERIC_SLOT( ty, fn, VT_CAT_4( 0, d3, d2, 7 ) ) VT_R1_7( ty, fn, d3, d2 ) #define VT_R2_0( ty, fn, d3 ) #define VT_R2_1( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 0 ) #define VT_R2_2( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 1 ) VT_R2_1( ty, fn, d3 ) #define VT_R2_3( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 2 ) VT_R2_2( ty, fn, d3 ) #define VT_R2_4( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 3 ) VT_R2_3( ty, fn, d3 ) #define VT_R2_5( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 4 ) VT_R2_4( ty, fn, d3 ) #define VT_R2_6( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 5 ) VT_R2_5( ty, fn, d3 ) #define VT_R2_7( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 6 ) VT_R2_6( ty, fn, d3 ) #define VT_R2_8( ty, fn, d3 ) VT_R1_8( ty, fn, d3, 7 ) VT_R2_7( ty, fn, d3 ) #define VT_R3_0( ty, fn ) #define VT_R3_1( ty, fn ) VT_R2_8( ty, fn, 0 ) #define VT_R3_2( ty, fn ) VT_R2_8( ty, fn, 1 ) VT_R3_1( ty, fn ) #define VT_R3_3( ty, fn ) VT_R2_8( ty, fn, 2 ) VT_R3_2( ty, fn ) #define VT_R3_4( ty, fn ) VT_R2_8( ty, fn, 3 ) VT_R3_3( ty, fn ) #define VT_R3_5( ty, fn ) VT_R2_8( ty, fn, 4 ) VT_R3_4( ty, fn ) #define VT_R3_6( ty, fn ) VT_R2_8( ty, fn, 5 ) VT_R3_5( ty, fn ) #define VT_R3_7( ty, fn ) VT_R2_8( ty, fn, 6 ) VT_R3_6( ty, fn ) #define VT_GENERIC_SLOTS( ty, fn ) \ VT_CAT( VT_R1_, VT_TEMPLATE_COUNT_D1 )( ty, fn, VT_TEMPLATE_COUNT_D3, VT_TEMPLATE_COUNT_D2 ) \ VT_CAT( VT_R2_, VT_TEMPLATE_COUNT_D2 )( ty, fn, VT_TEMPLATE_COUNT_D3 ) \ VT_CAT( VT_R3_, VT_TEMPLATE_COUNT_D3 )( ty, fn ) \ // Actual generic API macros. // vt_init must be handled as a special case because it could take one or two arguments, depending on whether CTX_TY // was defined. #define VT_ARG_3( _1, _2, _3, ... ) _3 #define vt_init( ... ) VT_ARG_3( __VA_ARGS__, vt_init_with_ctx, vt_init_without_ctx, )( __VA_ARGS__ ) #define vt_init_without_ctx( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_init_ ) )( table ) #define vt_init_with_ctx( table, ... ) _Generic( *( table ) \ VT_GENERIC_SLOTS( vt_table_, vt_init_ ) \ )( table, __VA_ARGS__ ) \ #define vt_init_clone( table, ... ) _Generic( *( table ) \ VT_GENERIC_SLOTS( vt_table_, vt_init_clone_ ) \ )( table, __VA_ARGS__ ) \ #define vt_size( table )_Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_size_ ) )( table ) #define vt_bucket_count( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_bucket_count_ ) )( table ) #define vt_is_end( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_is_end_ ) )( itr ) #define vt_insert( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_insert_ ) )( table, __VA_ARGS__ ) #define vt_get_or_insert( table, ... ) _Generic( *( table ) \ VT_GENERIC_SLOTS( vt_table_, vt_get_or_insert_ ) \ )( table, __VA_ARGS__ ) \ #define vt_get( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_get_ ) )( table, __VA_ARGS__ ) #define vt_erase( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_erase_ ) )( table, __VA_ARGS__ ) #define vt_next( itr ) _Generic( itr VT_GENERIC_SLOTS( vt_table_itr_, vt_next_ ) )( itr ) #define vt_erase_itr( table, ... ) _Generic( *( table ) \ VT_GENERIC_SLOTS( vt_table_, vt_erase_itr_ ) \ )( table, __VA_ARGS__ ) \ #define vt_reserve( table, ... ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_reserve_ ) )( table, __VA_ARGS__ ) #define vt_shrink( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_shrink_ ) )( table ) #define vt_first( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_first_ ) )( table ) #define vt_clear( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_clear_ ) )( table ) #define vt_cleanup( table ) _Generic( *( table ) VT_GENERIC_SLOTS( vt_table_, vt_cleanup_ ) )( table ) #endif #endif /*--------------------------------------------------------------------------------------------------------------------*/ /* Prefixed structs */ /*--------------------------------------------------------------------------------------------------------------------*/ #ifndef IMPLEMENTATION_MODE typedef struct { KEY_TY key; #ifdef VAL_TY VAL_TY val; #endif } VT_CAT( NAME, _bucket ); typedef struct { VT_CAT( NAME, _bucket ) *data; uint16_t *metadatum; uint16_t *metadata_end; // Iterators carry an internal end pointer so that NAME_is_end does not need the table to be // passed in as an argument. // This also allows for the zero-bucket-count check to occur once in NAME_first, rather than // repeatedly in NAME_is_end. size_t home_bucket; // SIZE_MAX if home bucket is unknown. } VT_CAT( NAME, _itr ); typedef struct { size_t key_count; size_t buckets_mask; // Rather than storing the bucket count directly, we store the bit mask used to reduce a hash // code or displacement-derived bucket index to the buckets array, i.e. the bucket count minus // one. // Consequently, a zero bucket count (i.e. when .metadata points to the placeholder) constitutes // a special case, represented by all bits unset (i.e. zero). VT_CAT( NAME, _bucket ) *buckets; uint16_t *metadata; // As described above, each metadatum consists of a 4-bit hash-code fragment (X), a 1-bit flag // indicating whether the key in this bucket begins a chain associated with the bucket (Y), and // an 11-bit value indicating the quadratic displacement of the next key in the chain (Z): // XXXXYZZZZZZZZZZZ. #ifdef CTX_TY CTX_TY ctx; #endif } NAME; #endif /*--------------------------------------------------------------------------------------------------------------------*/ /* Function prototypes */ /*--------------------------------------------------------------------------------------------------------------------*/ #if defined( HEADER_MODE ) || defined( IMPLEMENTATION_MODE ) #define VT_API_FN_QUALIFIERS #else #define VT_API_FN_QUALIFIERS static inline #endif #ifndef IMPLEMENTATION_MODE VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )( NAME * #ifdef CTX_TY , CTX_TY #endif ); VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )( NAME *, const NAME * #ifdef CTX_TY , CTX_TY #endif ); VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME * ); VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME * ); VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) ); VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )( NAME *, KEY_TY #ifdef VAL_TY , VAL_TY #endif ); VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )( NAME *, KEY_TY #ifdef VAL_TY , VAL_TY #endif ); VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )( const NAME *table, KEY_TY key ); VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *, KEY_TY ); VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) ); VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *, size_t ); VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME * ); VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( const NAME * ); VT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME * ); VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME * ); // Not an API function, but must be prototyped anyway because it is called by the inline NAME_erase_itr below. VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw ) ( NAME *, VT_CAT( NAME, _itr ) ); // Erases the key pointed to by itr and returns an iterator to the next key in the table. // This function must be inlined to ensure that the compiler optimizes away the NAME_fast_forward call if the returned // iterator is discarded. #ifdef __GNUC__ static inline __attribute__((always_inline)) #elif defined( _MSC_VER ) static __forceinline #else static inline #endif VT_CAT( NAME, _itr ) VT_CAT( NAME, _erase_itr )( NAME *table, VT_CAT( NAME, _itr ) itr ) { if( VT_CAT( NAME, _erase_itr_raw )( table, itr ) ) return VT_CAT( NAME, _next )( itr ); return itr; } #endif /*--------------------------------------------------------------------------------------------------------------------*/ /* Function implementations */ /*--------------------------------------------------------------------------------------------------------------------*/ #ifndef HEADER_MODE // Default settings. #ifndef MAX_LOAD #define MAX_LOAD 0.9 #endif #ifndef MALLOC_FN #ifdef CTX_TY #define MALLOC_FN vt_malloc_with_ctx #else #define MALLOC_FN vt_malloc #endif #endif #ifndef FREE_FN #ifdef CTX_TY #define FREE_FN vt_free_with_ctx #else #define FREE_FN vt_free #endif #endif #ifndef HASH_FN #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #ifdef _MSC_VER // In MSVC, the compound literal in the _Generic triggers a warning about unused local variables at /W4. #define HASH_FN \ _Pragma( "warning( push )" ) \ _Pragma( "warning( disable: 4189 )" ) \ _Generic( ( KEY_TY ){ 0 }, char *: vt_hash_string, const char*: vt_hash_string, default: vt_hash_integer ) \ _Pragma( "warning( pop )" ) #else #define HASH_FN _Generic( ( KEY_TY ){ 0 }, \ char *: vt_hash_string, \ const char*: vt_hash_string, \ default: vt_hash_integer \ ) #endif #else #error Hash function inference is only available in C11 and later. In C99, you need to define HASH_FN manually to \ vt_hash_integer, vt_hash_string, or your own custom function with the signature uint64_t ( KEY_TY ). #endif #endif #ifndef CMPR_FN #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #ifdef _MSC_VER #define CMPR_FN \ _Pragma( "warning( push )" ) \ _Pragma( "warning( disable: 4189 )" ) \ _Generic( ( KEY_TY ){ 0 }, char *: vt_cmpr_string, const char*: vt_cmpr_string, default: vt_cmpr_integer ) \ _Pragma( "warning( pop )" ) #else #define CMPR_FN _Generic( ( KEY_TY ){ 0 }, \ char *: vt_cmpr_string, \ const char*: vt_cmpr_string, \ default: vt_cmpr_integer \ ) #endif #else #error Comparison function inference is only available in C11 and later. In C99, you need to define CMPR_FN manually \ to vt_cmpr_integer, vt_cmpr_string, or your own custom function with the signature bool ( KEY_TY, KEY_TY ). #endif #endif VT_API_FN_QUALIFIERS void VT_CAT( NAME, _init )( NAME *table #ifdef CTX_TY , CTX_TY ctx #endif ) { table->key_count = 0; table->buckets_mask = 0x0000000000000000ull; table->buckets = NULL; table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum; #ifdef CTX_TY table->ctx = ctx; #endif } // For efficiency, especially in the case of a small table, the buckets array and metadata share the same dynamic memory // allocation: // +-----------------------------+-----+----------------+--------+ // | Buckets | Pad | Metadata | Excess | // +-----------------------------+-----+----------------+--------+ // Any allocated metadata array requires four excess elements to ensure that iteration functions, which read four // metadata at a time, never read beyond the end of it. // This function returns the offset of the beginning of the metadata, i.e. the size of the buckets array plus the // (usually zero) padding. // It assumes that the bucket count is not zero. static inline size_t VT_CAT( NAME, _metadata_offset )( NAME *table ) { // Use sizeof, rather than alignof, for C99 compatibility. return ( ( ( table->buckets_mask + 1 ) * sizeof( VT_CAT( NAME, _bucket ) ) + sizeof( uint16_t ) - 1 ) / sizeof( uint16_t ) ) * sizeof( uint16_t ); } // Returns the total allocation size, including the buckets array, padding, metadata, and excess metadata. // As above, this function assumes that the bucket count is not zero. static inline size_t VT_CAT( NAME, _total_alloc_size )( NAME *table ) { return VT_CAT( NAME, _metadata_offset )( table ) + ( table->buckets_mask + 1 + 4 ) * sizeof( uint16_t ); } VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _init_clone )( NAME *table, const NAME *source #ifdef CTX_TY , CTX_TY ctx #endif ) { table->key_count = source->key_count; table->buckets_mask = source->buckets_mask; #ifdef CTX_TY table->ctx = ctx; #endif if( !source->buckets_mask ) { table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum; table->buckets = NULL; return true; } void *allocation = MALLOC_FN( VT_CAT( NAME, _total_alloc_size )( table ) #ifdef CTX_TY , &table->ctx #endif ); if( VT_UNLIKELY( !allocation ) ) return false; table->buckets = (VT_CAT( NAME, _bucket ) *)allocation; table->metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( table ) ); memcpy( allocation, source->buckets, VT_CAT( NAME, _total_alloc_size )( table ) ); return true; } VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _size )( const NAME *table ) { return table->key_count; } VT_API_FN_QUALIFIERS size_t VT_CAT( NAME, _bucket_count )( const NAME *table ) { // If the bucket count is zero, buckets_mask will be zero, not the bucket count minus one. // We account for this special case by adding (bool)buckets_mask rather than one. return table->buckets_mask + (bool)table->buckets_mask; } VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _is_end )( VT_CAT( NAME, _itr ) itr ) { return itr.metadatum == itr.metadata_end; } // Finds the earliest empty bucket in which a key belonging to home_bucket can be placed, assuming that home_bucket // is already occupied. // The reason to begin the search at home_bucket, rather than the end of the existing chain, is that keys deleted from // other chains might have freed up buckets that could fall in this chain before the final key. // Returns true if an empty bucket within the range of the displacement limit was found, in which case the final two // pointer arguments contain the index of the empty bucket and its quadratic displacement from home_bucket. static inline bool VT_CAT( NAME, _find_first_empty )( NAME *table, size_t home_bucket, size_t *empty, uint16_t *displacement ) { *displacement = 1; size_t linear_dispacement = 1; while( true ) { *empty = ( home_bucket + linear_dispacement ) & table->buckets_mask; if( table->metadata[ *empty ] == VT_EMPTY ) return true; if( VT_UNLIKELY( ++*displacement == VT_DISPLACEMENT_MASK ) ) return false; linear_dispacement += *displacement; } } // Finds the key in the chain beginning in home_bucket after which to link a new key with displacement_to_empty // quadratic displacement and returns the index of the bucket containing that key. // Although the new key could simply be linked to the end of the chain, keeping the chain ordered by displacement // theoretically improves cache locality during lookups. static inline size_t VT_CAT( NAME, _find_insert_location_in_chain )( NAME *table, size_t home_bucket, uint16_t displacement_to_empty ) { size_t candidate = home_bucket; while( true ) { uint16_t displacement = table->metadata[ candidate ] & VT_DISPLACEMENT_MASK; if( displacement > displacement_to_empty ) return candidate; candidate = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; } } // Frees up a bucket occupied by a key not belonging there so that a new key belonging there can be placed there as the // beginning of a new chain. // This requires: // * Finding the previous key in the chain to which the occupying key belongs by rehashing it and then traversing the // chain. // * Disconnecting the key from the chain. // * Finding the appropriate empty bucket to which to move the key. // * Moving the key (and value) data to the empty bucket. // * Re-linking the key to the chain. // Returns true if the eviction succeeded, or false if no empty bucket to which to evict the occupying key could be // found within the displacement limit. static inline bool VT_CAT( NAME, _evict )( NAME *table, size_t bucket ) { // Find the previous key in chain. size_t home_bucket = HASH_FN( table->buckets[ bucket ].key ) & table->buckets_mask; size_t prev = home_bucket; while( true ) { size_t next = ( home_bucket + vt_quadratic( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ) ) & table->buckets_mask; if( next == bucket ) break; prev = next; } // Disconnect the key from chain. table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | ( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ); // Find the empty bucket to which to move the key. size_t empty; uint16_t displacement; if( VT_UNLIKELY( !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement ) ) ) return false; // Find the key in the chain after which to link the moved key. prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement ); // Move the key (and value) data. table->buckets[ empty ] = table->buckets[ bucket ]; // Re-link the key to the chain from its new bucket. table->metadata[ empty ] = ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) | ( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ); table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement; return true; } // Returns an end iterator, i.e. any iterator for which .metadatum == .metadata_end. // This function just cleans up the library code in functions that return an end iterator as a failure indicator. static inline VT_CAT( NAME, _itr ) VT_CAT( NAME, _end_itr )( void ) { VT_CAT( NAME, _itr ) itr = { NULL, NULL, NULL, 0 }; return itr; } // Inserts a key, optionally replacing the existing key if it already exists. // There are two main cases that must be handled: // * If the key's home bucket is empty or occupied by a key that does not belong there, then the key is inserted there, // evicting the occupying key if there is one. // * Otherwise, the chain of keys beginning at the home bucket is (if unique is false) traversed in search of a matching // key. // If none is found, then the new key is inserted at the earliest available bucket, per quadratic probing from the // home bucket, and then linked to the chain in a manner that maintains its quadratic order. // The unique argument tells the function whether to skip searching for the key before inserting it (on rehashing, this // step is unnecessary). // The replace argument tells the function whether to replace an existing key. // If replace is true, the function returns an iterator to the inserted key, or an end iterator if the key was not // inserted because of the maximum load factor or displacement limit constraints. // If replace is false, then the return value is as described above, except that if the key already exists, the function // returns an iterator to the existing key. static inline VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert_raw )( NAME *table, KEY_TY key, #ifdef VAL_TY VAL_TY *val, #endif bool unique, bool replace ) { uint64_t hash = HASH_FN( key ); uint16_t hashfrag = vt_hashfrag( hash ); size_t home_bucket = hash & table->buckets_mask; // Case 1: The home bucket is empty or contains a key that doesn't belong there. // This case also implicitly handles the case of a zero bucket count, since home_bucket will be zero and metadata[ 0 ] // will be the empty placeholder. // In that scenario, the zero buckets_mask triggers the below load-factor check. if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) ) { if( // Load-factor check. VT_UNLIKELY( table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD ) || // Vacate the home bucket if it contains a key. ( table->metadata[ home_bucket ] != VT_EMPTY && VT_UNLIKELY( !VT_CAT( NAME, _evict )( table, home_bucket ) ) ) ) return VT_CAT( NAME, _end_itr )(); table->buckets[ home_bucket ].key = key; #ifdef VAL_TY table->buckets[ home_bucket ].val = *val; #endif table->metadata[ home_bucket ] = hashfrag | VT_IN_HOME_BUCKET_MASK | VT_DISPLACEMENT_MASK; ++table->key_count; VT_CAT( NAME, _itr ) itr = { table->buckets + home_bucket, table->metadata + home_bucket, table->metadata + table->buckets_mask + 1, // Iteration stopper (i.e. the first of the four excess metadata). home_bucket }; return itr; } // Case 2: The home bucket contains the beginning of a chain. // Optionally, check the existing chain. if( !unique ) { size_t bucket = home_bucket; while( true ) { if( ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag && VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) ) ) { if( replace ) { #ifdef KEY_DTOR_FN KEY_DTOR_FN( table->buckets[ bucket ].key ); #endif table->buckets[ bucket ].key = key; #ifdef VAL_TY #ifdef VAL_DTOR_FN VAL_DTOR_FN( table->buckets[ bucket ].val ); #endif table->buckets[ bucket ].val = *val; #endif } VT_CAT( NAME, _itr ) itr = { table->buckets + bucket, table->metadata + bucket, table->metadata + table->buckets_mask + 1, home_bucket }; return itr; } uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK; if( displacement == VT_DISPLACEMENT_MASK ) break; bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; } } size_t empty; uint16_t displacement; if( VT_UNLIKELY( // Load-factor check. table->key_count + 1 > VT_CAT( NAME, _bucket_count )( table ) * MAX_LOAD || // Find the earliest empty bucket, per quadratic probing. !VT_CAT( NAME, _find_first_empty )( table, home_bucket, &empty, &displacement ) ) ) return VT_CAT( NAME, _end_itr )(); // Insert the new key (and value) in the empty bucket and link it to the chain. size_t prev = VT_CAT( NAME, _find_insert_location_in_chain )( table, home_bucket, displacement ); table->buckets[ empty ].key = key; #ifdef VAL_TY table->buckets[ empty ].val = *val; #endif table->metadata[ empty ] = hashfrag | ( table->metadata[ prev ] & VT_DISPLACEMENT_MASK ); table->metadata[ prev ] = ( table->metadata[ prev ] & ~VT_DISPLACEMENT_MASK ) | displacement; ++table->key_count; VT_CAT( NAME, _itr ) itr = { table->buckets + empty, table->metadata + empty, table->metadata + table->buckets_mask + 1, home_bucket }; return itr; } // Resizes the bucket array. // This function assumes that bucket_count is a power of two and large enough to accommodate all keys without violating // the maximum load factor. // Returns false in the case of allocation failure. // As this function is called very rarely in _insert and _get_or_insert, ideally it should not be inlined into those // functions. // In testing, the no-inline approach showed a performance benefit when inserting existing keys (i.e. replacing). #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" // Silence warning about combining noinline with static inline. __attribute__((noinline)) static inline #elif defined( _MSC_VER ) __declspec(noinline) static inline #else static inline #endif bool VT_CAT( NAME, _rehash )( NAME *table, size_t bucket_count ) { // The attempt to resize the bucket array and rehash the keys must occur inside a loop that incrementally doubles the // target bucket count because a failure could theoretically occur at any load factor due to the displacement limit. while( true ) { NAME new_table = { 0, bucket_count - 1, NULL, NULL #ifdef CTX_TY , table->ctx #endif }; void *allocation = MALLOC_FN( VT_CAT( NAME, _total_alloc_size )( &new_table ) #ifdef CTX_TY , &new_table.ctx #endif ); if( VT_UNLIKELY( !allocation ) ) return false; new_table.buckets = (VT_CAT( NAME, _bucket ) *)allocation; new_table.metadata = (uint16_t *)( (unsigned char *)allocation + VT_CAT( NAME, _metadata_offset )( &new_table ) ); memset( new_table.metadata, 0x00, ( bucket_count + 4 ) * sizeof( uint16_t ) ); // Iteration stopper at the end of the actual metadata array (i.e. the first of the four excess metadata). new_table.metadata[ bucket_count ] = 0x01; for( size_t bucket = 0; bucket < VT_CAT( NAME, _bucket_count )( table ); ++bucket ) if( table->metadata[ bucket ] != VT_EMPTY ) { VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )( &new_table, table->buckets[ bucket ].key, #ifdef VAL_TY &table->buckets[ bucket ].val, #endif true, false ); if( VT_UNLIKELY( VT_CAT( NAME, _is_end )( itr ) ) ) break; } // If a key could not be reinserted due to the displacement limit, double the bucket count and retry. if( VT_UNLIKELY( new_table.key_count < table->key_count ) ) { FREE_FN( new_table.buckets, VT_CAT( NAME, _total_alloc_size )( &new_table ) #ifdef CTX_TY , &new_table.ctx #endif ); bucket_count *= 2; continue; } if( table->buckets_mask ) FREE_FN( table->buckets, VT_CAT( NAME, _total_alloc_size )( table ) #ifdef CTX_TY , &table->ctx #endif ); *table = new_table; return true; } } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // Inserts a key, replacing the existing key if it already exists. // This function wraps insert_raw in a loop that handles growing and rehashing the table if a new key cannot be inserted // because of the maximum load factor or displacement limit constraints. // Returns an iterator to the inserted key, or an end iterator in the case of allocation failure. VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _insert )( NAME *table, KEY_TY key #ifdef VAL_TY , VAL_TY val #endif ) { while( true ) { VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )( table, key, #ifdef VAL_TY &val, #endif false, true ); if( // Lookup succeeded, in which case itr points to the found key. VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) || // Lookup failed and rehash also fails, in which case itr is an end iterator. VT_UNLIKELY( !VT_CAT( NAME, _rehash )( table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT ) ) ) return itr; } } // Same as NAME_insert, except that if the key already exists, no insertion occurs and the function returns an iterator // to the existing key. VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get_or_insert )( NAME *table, KEY_TY key #ifdef VAL_TY , VAL_TY val #endif ) { while( true ) { VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _insert_raw )( table, key, #ifdef VAL_TY &val, #endif false, false ); if( // Lookup succeeded, in which case itr points to the found key. VT_LIKELY( !VT_CAT( NAME, _is_end )( itr ) ) || // Lookup failed and rehash also fails, in which case itr is an end iterator. VT_UNLIKELY( !VT_CAT( NAME, _rehash )( table, table->buckets_mask ? VT_CAT( NAME, _bucket_count )( table ) * 2 : VT_MIN_NONZERO_BUCKET_COUNT ) ) ) return itr; } } // Returns an iterator pointing to the specified key, or an end iterator if the key does not exist. VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _get )( const NAME *table, KEY_TY key ) { uint64_t hash = HASH_FN( key ); size_t home_bucket = hash & table->buckets_mask; // If the home bucket is empty or contains a key that does not belong there, then our key does not exist. // This check also implicitly handles the case of a zero bucket count, since home_bucket will be zero and // metadata[ 0 ] will be the empty placeholder. if( !( table->metadata[ home_bucket ] & VT_IN_HOME_BUCKET_MASK ) ) return VT_CAT( NAME, _end_itr )(); // Traverse the chain of keys belonging to the home bucket. uint16_t hashfrag = vt_hashfrag( hash ); size_t bucket = home_bucket; while( true ) { if( ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ) == hashfrag && VT_LIKELY( CMPR_FN( table->buckets[ bucket ].key, key ) ) ) { VT_CAT( NAME, _itr ) itr = { table->buckets + bucket, table->metadata + bucket, table->metadata + table->buckets_mask + 1, home_bucket }; return itr; } uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK; if( displacement == VT_DISPLACEMENT_MASK ) return VT_CAT( NAME, _end_itr )(); bucket = ( home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; } } // Erases the key pointed to by the specified iterator. // The erasure always occurs at the end of the chain to which the key belongs. // If the key to be erased is not the last in the chain, it is swapped with the last so that erasure occurs at the end. // This helps keep a chain's keys close to their home bucket for the sake of cache locality. // Returns true if, in the case of iteration from first to end, NAME_next should now be called on the iterator to find // the next key. // This return value is necessary because at the iterator location, the erasure could result in an empty bucket, a // bucket containing a moved key already visited during the iteration, or a bucket containing a moved key not yet // visited. VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase_itr_raw )( NAME *table, VT_CAT( NAME, _itr ) itr ) { --table->key_count; size_t itr_bucket = itr.metadatum - table->metadata; // For now, we only call the value's destructor because the key may need to be hashed below to determine the home // bucket. #ifdef VAL_DTOR_FN VAL_DTOR_FN( table->buckets[ itr_bucket ].val ); #endif // Case 1: The key is the only one in its chain, so just remove it. if( table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK && ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK ) { #ifdef KEY_DTOR_FN KEY_DTOR_FN( table->buckets[ itr_bucket ].key ); #endif table->metadata[ itr_bucket ] = VT_EMPTY; return true; } // Case 2 and 3 require that we know the key's home bucket, which the iterator may not have recorded. if( itr.home_bucket == SIZE_MAX ) { if( table->metadata[ itr_bucket ] & VT_IN_HOME_BUCKET_MASK ) itr.home_bucket = itr_bucket; else itr.home_bucket = HASH_FN( table->buckets[ itr_bucket ].key ) & table->buckets_mask; } // The key can now be safely destructed for cases 2 and 3. #ifdef KEY_DTOR_FN KEY_DTOR_FN( table->buckets[ itr_bucket ].key ); #endif // Case 2: The key is the last in a multi-key chain. // Traverse the chain from the beginning and find the penultimate key. // Then disconnect the key and erase. if( ( table->metadata[ itr_bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK ) { size_t bucket = itr.home_bucket; while( true ) { uint16_t displacement = table->metadata[ bucket ] & VT_DISPLACEMENT_MASK; size_t next = ( itr.home_bucket + vt_quadratic( displacement ) ) & table->buckets_mask; if( next == itr_bucket ) { table->metadata[ bucket ] |= VT_DISPLACEMENT_MASK; table->metadata[ itr_bucket ] = VT_EMPTY; return true; } bucket = next; } } // Case 3: The chain has multiple keys, and the key is not the last one. // Traverse the chain from the key to be erased and find the last and penultimate keys. // Disconnect the last key from the chain, and swap it with the key to erase. size_t bucket = itr_bucket; while( true ) { size_t prev = bucket; bucket = ( itr.home_bucket + vt_quadratic( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) ) & table->buckets_mask; if( ( table->metadata[ bucket ] & VT_DISPLACEMENT_MASK ) == VT_DISPLACEMENT_MASK ) { table->buckets[ itr_bucket ] = table->buckets[ bucket ]; table->metadata[ itr_bucket ] = ( table->metadata[ itr_bucket ] & ~VT_HASH_FRAG_MASK ) | ( table->metadata[ bucket ] & VT_HASH_FRAG_MASK ); table->metadata[ prev ] |= VT_DISPLACEMENT_MASK; table->metadata[ bucket ] = VT_EMPTY; // Whether the iterator should be advanced depends on whether the key moved to the iterator bucket came from // before or after that bucket. // In the former case, the iteration would already have hit the moved key, so the iterator should still be // advanced. if( bucket > itr_bucket ) return false; return true; } } } // Erases the specified key, if it exists. // Returns true if a key was erased. VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _erase )( NAME *table, KEY_TY key ) { VT_CAT( NAME, _itr ) itr = VT_CAT( NAME, _get)( table, key ); if( VT_CAT( NAME, _is_end )( itr ) ) return false; VT_CAT( NAME, _erase_itr_raw )( table, itr ); return true; } // Finds the first occupied bucket at or after the bucket pointed to by itr. // This function scans four buckets at a time, ideally using intrinsics. static inline void VT_CAT( NAME, _fast_forward )( VT_CAT( NAME, _itr ) *itr ) { while( true ) { uint64_t metadata; memcpy( &metadata, itr->metadatum, sizeof( uint64_t ) ); if( metadata ) { int offset = vt_first_nonzero_uint16( metadata ); itr->data += offset; itr->metadatum += offset; itr->home_bucket = SIZE_MAX; return; } itr->data += 4; itr->metadatum += 4; } } VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _next )( VT_CAT( NAME, _itr ) itr ) { ++itr.data; ++itr.metadatum; VT_CAT( NAME, _fast_forward )( &itr ); return itr; } // Returns the minimum bucket count required to accommodate a certain number of keys, which is governed by the maximum // load factor. static inline size_t VT_CAT( NAME, _min_bucket_count_for_size )( size_t size ) { if( size == 0 ) return 0; // Round up to a power of two. size_t bucket_count = VT_MIN_NONZERO_BUCKET_COUNT; while( size > bucket_count * MAX_LOAD ) bucket_count *= 2; return bucket_count; } VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _reserve )( NAME *table, size_t size ) { size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( size ); if( bucket_count <= VT_CAT( NAME, _bucket_count )( table ) ) return true; return VT_CAT( NAME, _rehash )( table, bucket_count ); } VT_API_FN_QUALIFIERS bool VT_CAT( NAME, _shrink )( NAME *table ) { size_t bucket_count = VT_CAT( NAME, _min_bucket_count_for_size )( table->key_count ); if( bucket_count == VT_CAT( NAME, _bucket_count )( table ) ) // Shrink unnecessary. return true; if( bucket_count == 0 ) { FREE_FN( table->buckets, VT_CAT( NAME, _total_alloc_size )( table ) #ifdef CTX_TY , &table->ctx #endif ); table->buckets_mask = 0x0000000000000000ull; table->metadata = (uint16_t *)&vt_empty_placeholder_metadatum; return true; } return VT_CAT( NAME, _rehash )( table, bucket_count ); } VT_API_FN_QUALIFIERS VT_CAT( NAME, _itr ) VT_CAT( NAME, _first )( const NAME *table ) { if( !table->key_count ) return VT_CAT( NAME, _end_itr )(); VT_CAT( NAME, _itr ) itr = { table->buckets, table->metadata, table->metadata + table->buckets_mask + 1, SIZE_MAX }; VT_CAT( NAME, _fast_forward )( &itr ); return itr; } VT_API_FN_QUALIFIERS void VT_CAT( NAME, _clear )( NAME *table ) { if( !table->key_count ) return; for( size_t i = 0; i < VT_CAT( NAME, _bucket_count )( table ); ++i ) { if( table->metadata[ i ] != VT_EMPTY ) { #ifdef KEY_DTOR_FN KEY_DTOR_FN( table->buckets[ i ].key ); #endif #ifdef VAL_DTOR_FN VAL_DTOR_FN( table->buckets[ i ].val ); #endif } table->metadata[ i ] = VT_EMPTY; } table->key_count = 0; } VT_API_FN_QUALIFIERS void VT_CAT( NAME, _cleanup )( NAME *table ) { if( !table->buckets_mask ) return; #if defined( KEY_DTOR_FN ) || defined( VAL_DTOR_FN ) VT_CAT( NAME, _clear )( table ); #endif FREE_FN( table->buckets, VT_CAT( NAME, _total_alloc_size )( table ) #ifdef CTX_TY , &table->ctx #endif ); VT_CAT( NAME, _init )( table #ifdef CTX_TY , table->ctx #endif ); } #endif /*--------------------------------------------------------------------------------------------------------------------*/ /* Wrapper types and functions for the C11 generic API */ /*--------------------------------------------------------------------------------------------------------------------*/ #if defined(__STDC_VERSION__) && \ __STDC_VERSION__ >= 201112L && \ !defined( IMPLEMENTATION_MODE ) && \ !defined( VT_NO_C11_GENERIC_API ) \ typedef NAME VT_CAT( vt_table_, VT_TEMPLATE_COUNT ); typedef VT_CAT( NAME, _itr ) VT_CAT( vt_table_itr_, VT_TEMPLATE_COUNT ); static inline void VT_CAT( vt_init_, VT_TEMPLATE_COUNT )( NAME *table #ifdef CTX_TY , CTX_TY ctx #endif ) { VT_CAT( NAME, _init )( table #ifdef CTX_TY , ctx #endif ); } static inline bool VT_CAT( vt_init_clone_, VT_TEMPLATE_COUNT )( NAME *table, const NAME* source #ifdef CTX_TY , CTX_TY ctx #endif ) { return VT_CAT( NAME, _init_clone )( table, source #ifdef CTX_TY , ctx #endif ); } static inline size_t VT_CAT( vt_size_, VT_TEMPLATE_COUNT )( const NAME *table ) { return VT_CAT( NAME, _size )( table ); } static inline size_t VT_CAT( vt_bucket_count_, VT_TEMPLATE_COUNT )( const NAME *table ) { return VT_CAT( NAME, _bucket_count )( table ); } static inline bool VT_CAT( vt_is_end_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) { return VT_CAT( NAME, _is_end )( itr ); } static inline VT_CAT( NAME, _itr ) VT_CAT( vt_insert_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key #ifdef VAL_TY , VAL_TY val #endif ) { return VT_CAT( NAME, _insert )( table, key #ifdef VAL_TY , val #endif ); } static inline VT_CAT( NAME, _itr ) VT_CAT( vt_get_or_insert_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key #ifdef VAL_TY , VAL_TY val #endif ) { return VT_CAT( NAME, _get_or_insert )( table, key #ifdef VAL_TY , val #endif ); } static inline VT_CAT( NAME, _itr ) VT_CAT( vt_get_, VT_TEMPLATE_COUNT )( const NAME *table, KEY_TY key ) { return VT_CAT( NAME, _get )( table, key ); } static inline bool VT_CAT( vt_erase_, VT_TEMPLATE_COUNT )( NAME *table, KEY_TY key ) { return VT_CAT( NAME, _erase )( table, key ); } static inline VT_CAT( NAME, _itr ) VT_CAT( vt_next_, VT_TEMPLATE_COUNT )( VT_CAT( NAME, _itr ) itr ) { return VT_CAT( NAME, _next )( itr ); } static inline VT_CAT( NAME, _itr ) VT_CAT( vt_erase_itr_, VT_TEMPLATE_COUNT )( NAME *table, VT_CAT( NAME, _itr ) itr ) { return VT_CAT( NAME, _erase_itr )( table, itr ); } static inline bool VT_CAT( vt_reserve_, VT_TEMPLATE_COUNT )( NAME *table, size_t bucket_count ) { return VT_CAT( NAME, _reserve )( table, bucket_count ); } static inline bool VT_CAT( vt_shrink_, VT_TEMPLATE_COUNT )( NAME *table ) { return VT_CAT( NAME, _shrink )( table ); } static inline VT_CAT( NAME, _itr ) VT_CAT( vt_first_, VT_TEMPLATE_COUNT )( const NAME *table ) { return VT_CAT( NAME, _first )( table ); } static inline void VT_CAT( vt_clear_, VT_TEMPLATE_COUNT )( NAME *table ) { VT_CAT( NAME, _clear )( table ); } static inline void VT_CAT( vt_cleanup_, VT_TEMPLATE_COUNT )( NAME *table ) { VT_CAT( NAME, _cleanup )( table ); } // Increment the template counter. #if VT_TEMPLATE_COUNT_D1 == 0 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 1 #elif VT_TEMPLATE_COUNT_D1 == 1 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 2 #elif VT_TEMPLATE_COUNT_D1 == 2 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 3 #elif VT_TEMPLATE_COUNT_D1 == 3 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 4 #elif VT_TEMPLATE_COUNT_D1 == 4 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 5 #elif VT_TEMPLATE_COUNT_D1 == 5 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 6 #elif VT_TEMPLATE_COUNT_D1 == 6 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 7 #elif VT_TEMPLATE_COUNT_D1 == 7 #undef VT_TEMPLATE_COUNT_D1 #define VT_TEMPLATE_COUNT_D1 0 #if VT_TEMPLATE_COUNT_D2 == 0 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 1 #elif VT_TEMPLATE_COUNT_D2 == 1 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 2 #elif VT_TEMPLATE_COUNT_D2 == 2 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 3 #elif VT_TEMPLATE_COUNT_D2 == 3 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 4 #elif VT_TEMPLATE_COUNT_D2 == 4 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 5 #elif VT_TEMPLATE_COUNT_D2 == 5 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 6 #elif VT_TEMPLATE_COUNT_D2 == 6 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 7 #elif VT_TEMPLATE_COUNT_D2 == 7 #undef VT_TEMPLATE_COUNT_D2 #define VT_TEMPLATE_COUNT_D2 0 #if VT_TEMPLATE_COUNT_D3 == 0 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 1 #elif VT_TEMPLATE_COUNT_D3 == 1 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 2 #elif VT_TEMPLATE_COUNT_D3 == 2 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 3 #elif VT_TEMPLATE_COUNT_D3 == 3 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 4 #elif VT_TEMPLATE_COUNT_D3 == 4 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 5 #elif VT_TEMPLATE_COUNT_D3 == 5 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 6 #elif VT_TEMPLATE_COUNT_D3 == 6 #undef VT_TEMPLATE_COUNT_D3 #define VT_TEMPLATE_COUNT_D3 7 #elif VT_TEMPLATE_COUNT_D3 == 7 #error Sorry, the number of template instances is limited to 511. Define VT_NO_C11_GENERIC_API globally and use the \ C99 prefixed function API to circumvent this restriction. #endif #endif #endif #endif #undef NAME #undef KEY_TY #undef VAL_TY #undef HASH_FN #undef CMPR_FN #undef MAX_LOAD #undef KEY_DTOR_FN #undef VAL_DTOR_FN #undef CTX_TY #undef MALLOC_FN #undef FREE_FN #undef HEADER_MODE #undef IMPLEMENTATION_MODE #undef VT_API_FN_QUALIFIERS ceccomp-4.0/include/main.h000066400000000000000000000015461514205130000155030ustar00rootroot00000000000000#ifndef MAIN_H #define MAIN_H // clang-format off #include #include #include #include #include #include #include #include #include "help.h" // clang-format on typedef struct sock_fprog fprog; typedef struct sock_filter filter; typedef struct seccomp_data seccomp_data; typedef struct ptrace_syscall_info syscall_info; typedef struct { const char *start; uint32_t len; } string_t; #define STARTWITH(str, token) (!strncmp (str, token, strlen (token))) #define LIKELY(x) __builtin_expect (!!(x), 1) #define UNLIKELY(x) __builtin_expect (!!(x), 0) #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) #define LITERAL_STRLEN(str) (ARRAY_SIZE (str) - 1) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif ceccomp-4.0/include/probe.h000066400000000000000000000002361514205130000156610ustar00rootroot00000000000000#ifndef PROBE_H #define PROBE_H #include #include #include extern void probe (char *argv[], FILE *fp, bool quiet); #endif ceccomp-4.0/include/resolver/000077500000000000000000000000001514205130000162415ustar00rootroot00000000000000ceccomp-4.0/include/resolver/render.h000066400000000000000000000002251514205130000176700ustar00rootroot00000000000000#ifndef RENDER_H #define RENDER_H #include "utils/vector.h" #include extern void render (vector_t *v_code, uint32_t scmp_arch); #endif ceccomp-4.0/include/resolver/resolver.h000066400000000000000000000004671514205130000202620ustar00rootroot00000000000000#ifndef RESOLVER_H #define RESOLVER_H #include "utils/vector.h" #include #include #define _SCMP_ACT_TRAP(x) (SCMP_ACT_TRAP | ((x) & 0x0000ffffU)) // see vector.h for vector details // return false if ok, return true if error occurs extern bool resolver (vector_t *code_ptr_v); #endif ceccomp-4.0/include/trace.h000066400000000000000000000006071514205130000156520ustar00rootroot00000000000000#ifndef TRACE_H #define TRACE_H #include #include #include #include typedef struct user_regs_struct regs; typedef struct ptrace_syscall_info syscall_info; extern uint32_t program_trace (char *argv[], FILE *output_fp, bool quiet, bool oneshot); extern void pid_trace (int pid, bool seize, bool quiet); #endif ceccomp-4.0/include/utils/000077500000000000000000000000001514205130000155405ustar00rootroot00000000000000ceccomp-4.0/include/utils/arch_trans.h000066400000000000000000000007001514205130000200320ustar00rootroot00000000000000#ifndef ARCH_TRANS_H #define ARCH_TRANS_H #include "lexical/token.h" #include "main.h" #include #include extern uint32_t internal_arch_to_scmp_arch (uint32_t internal_arch); extern uint32_t scmp_arch_to_internal_arch (uint32_t scmp_arch); extern uint32_t str_to_scmp_arch (const char *str); extern token_type str_to_internal_arch (const char *str); extern const string_t *scmp_arch_to_str (uint32_t scmp_arch); #endif ceccomp-4.0/include/utils/color.h000066400000000000000000000024001514205130000170230ustar00rootroot00000000000000#ifndef COLOR_H #define COLOR_H #include "parse_args.h" #include #include extern bool color_enable; extern bool log_color_enable; extern void set_color (color_mode_t color, FILE *output); extern void push_color (bool enable); extern void pop_color (void); #define CLR "\x1b[0m" #define REDCLR "\x1b[31m" #define GREENCLR "\x1b[32m" #define YELLOWCLR "\x1b[33m" #define BLUECLR "\x1b[34m" #define CYANCLR "\x1b[36m" #define PURPLECLR "\x1b[95m" #define LIGHTCLR "\x1b[90m" #define BRIGHT_YELLOWCLR "\x1b[93m" #define BRIGHT_BLUECLR "\x1b[94m" #define BRIGHT_CYANCLR "\x1b[96m" #define RED(str) ((color_enable) ? (REDCLR str CLR) : str) #define GREEN(str) ((color_enable) ? (GREENCLR str CLR) : str) #define YELLOW(str) ((color_enable) ? (YELLOWCLR str CLR) : str) #define BRIGHT_YELLOW(str) ((color_enable) ? (BRIGHT_YELLOWCLR str CLR) : str) #define BLUE(str) ((color_enable) ? (BLUECLR str CLR) : str) #define BRIGHT_BLUE(str) ((color_enable) ? (BRIGHT_BLUECLR str CLR) : str) #define CYAN(str) ((color_enable) ? (CYANCLR str CLR) : str) #define BRIGHT_CYAN(str) ((color_enable) ? (BRIGHT_CYANCLR str CLR) : str) #define PURPLE(str) ((color_enable) ? (PURPLECLR str CLR) : str) #define LIGHT(str) ((color_enable) ? (LIGHTCLR str CLR) : str) #endif ceccomp-4.0/include/utils/error.h000066400000000000000000000167171514205130000170560ustar00rootroot00000000000000#ifndef ERROR_H #define ERROR_H #include "i18n.h" // probe #define M_PROBE_TERMINATED _ ("Found errors when assembling BPF from trace") #define M_REQUEST_TMPFILE_FAILED _ ("Failed to request a temporary file: %s") // emu #define M_EMU_TERMINATED \ _ ("Found errors when assembling text before emulating") #define M_INPUT_SYS_NR _ ("Please input syscall_nr (syscall name or number)") #define M_INVALID_SYSNR _ ("Invalid syscall_nr") // asm #define M_ASM_TERMINATED _ ("Found errors when assembling") #define M_STATEMENT_OVERFLOW _ ("Input file has more than 1024 statements!") // disasm #define M_DISASM_TERMINATED _ ("Found fatal errors when disassembling") #define M_NO_FILTER _ ("The input is empty") #define M_TOO_LARGE_INPUT \ _ ("The input is larger than 1024 filters! Perhaps inputting a wrong " \ "file?") #define M_INPUT_HAS_LEFTOVER \ _ ("%d byte(s) at the end of input could not fit into a filter") // trace #define M_START_TRACING _ ("Start tracing process %d") #define M_PEEKDATA_FAILED_ADR _ ("Peekdata failed at %p") #define M_EXECV_ERR _ ("Failed to execute new program") #define M_PROCESS_FORK _ ("Process %d spawned a new pid %d") #define M_PROCESS_EXIT _ ("Process %d exited") #define M_PARSE_PID_BPF _ ("Parsing seccomp filter loaded in process %d") #define M_PID_BPF_LOAD_FAIL _ ("Process %d failed to load seccomp filter") #define M_PROCFS_NOT_ACCESSIBLE _ ("Procfs not accessible, unable to perform") #define M_NOT_AN_CBPF _ ("Found unresolvable non-classic BPF, skipping") #define M_SEIZING_KERNEL_THREAD _ ("Kernel thread can not be seized") #define M_NO_FILTER_FOUND _ ("No seccomp filters found in process %d") #define M_TARGET_TRACED_BY \ _ ("Target process is already being traced by process %d") #define M_FOUND_STRICT_MODE \ _ ("Process %d loaded strict seccomp mode, which only allows read, " \ "write, exit_group and sigreturn!") #define M_UNKNOWN_GETFILTER_ERR _ ("Unknown error when get filter: %s") #define M_UNKNOWN_SEIZE_ERR _ ("Unknown error when seizing process: %s") #define M_SEIZE_NONEXIST_PROC _ ("The process to seize does not exist") // no translation due to terms #define ACTION_GET_FILTER "ptrace get seccomp filters" #define ACTION_PTRACE_SEIZE "ptrace seizing" #define M_CAP_SYS_PTRACE_OR_KTHREAD \ _ ("perhaps seizing kthread or lacking CAP_SYS_PTRACE") #define M_REQUIRE_CAP_SYS_ADMIN \ _ ("Run with CAP_SYS_ADMIN capability to fetch seccomp filters") #define M_REQUIRE_CAP_SYS_PTRACE \ _ ("Run with CAP_SYS_PTRACE capability to seize a foreign process") #define M_CANNOT_WORK_FROM_32_TO_64 \ _ ("32-bit tracer can not ptrace 64-bit tracee") #define M_TRACEE_ARCH_NOT_SUPPORTED \ _ ("libseccomp does not support the tracee's arch (%#x)") #define M_CAP_SYS_ADMIN_OR_IN_SECCOMP \ _ ("perhaps lacking CAP_SYS_ADMIN or ceccomp is in seccomp mode") #define M_GET_FILTER_UNSUPPORTED \ _ ("PTRACE_GET_SECCOMP_FILTER is not supported on your system") #define M_CECCOMP_IN_SECCOMP \ _ ("Ceccomp is in seccomp mode, fetching seccomp filters of other process " \ "is not permitted") #define M_GET_FILTER_UNSUPPORTED_OR_NO_FILTER \ _ ("perhaps PTRACE_GET_SECCOMP_FILTER is not supported or no seccomp " \ "filter in target process") // parse_args #define M_INVALID_COLOR_MODE _ ("Invalid color mode") #define M_INVALID_FMT_MODE _ ("Invalid format mode") #define M_INVALID_NUMBER _ ("Invalid number") #define M_UNABLE_OPEN_FILE _ ("Unable to open file %s: %s") // read_source #define M_READ_FAIL _ ("Reading input failed: %s") #define M_FOUND_SUS_ZERO \ _ ("Found '\\0' at file offset %lu, perhaps it's not a text file?") #define M_FOUND_SUS_NO_LF \ _ ("No line break in source file, perhaps it's not a text file?") #define M_FOUND_SUS_LINE \ _ ("Line %u has more than %u bytes, perhaps the input is not a text file?") #define M_FILE_TOO_LARGE _ ("The input file is greater than 1 MiB!") #define M_LINES_TOO_MANY \ _ ("Found more than 4096 lines of text, perhaps it's not for ceccomp?") // hash #define M_CANNOT_FIND_LABEL _ ("Can not find label declaration") // parser #define M_UNEXPECT_TOKEN _ ("Unexpected token") #define M_DUPLICATED_LABEL _ ("Found duplicated label declaration") #define M_NUMBER_OVERFLOW _ ("Input number exceeds 32-bit range") #define M_EXPECT_OPERATOR _ ("Expect operator") #define M_EXPECT_RIGHT_VAR _ ("Expect rvalue") #define M_EXPECT_RETURN_VAL _ ("Expect return value") #define M_EXPECT_NUMBER _ ("Expect number") #define M_EXPECT_PAREN _ ("Expect parenthesis") #define M_EXPECT_BRACKET _ ("Expect bracket") #define M_EXPECT_COMPARTOR _ ("Expect comparator") #define M_EXPECT_LABEL _ ("Expect label") #define M_EXPECT_SYSCALL _ ("Expect valid syscall name") #define M_EXPECT_ARCH \ _ ("Architecture token does not match any libseccomp arch, perhaps system " \ "libseccomp is too old") // EXPECT_SYSCALL also use in resolver #define M_EXPECT_GOTO _ ("Expect 'goto'") #define M_EXPECT_A _ ("Expect '$A'") #define M_EXPECT_ELSE _ ("Expect 'else'") // resolver and check_prog #define M_RIGHT_SHOULD_BE_A _ ("Rvalue should be '$A'") #define M_RIGHT_CAN_NOT_BE_A _ ("Rvalue can not be '$A'") #define M_RIGHT_CAN_NOT_BE_X _ ("Rvalue can not be '$X'") #define M_RIGHT_SHOULD_BE_A_OR_X _ ("Rvalue should be '$A' or '$X'") #define M_RIGHT_SHOULD_BE_X_OR_NUM _ ("Rvalue should be '$X' or number") #define M_OPERATOR_SHOULD_BE_EQUAL _ ("Operator should be '='") #define M_LEFT_SHOULD_BE_A _ ("Lvalue should be '$A'") #define M_INVALID_ATTR_LOAD \ _ ("seccomp_data load offset must be 4-byte aligned") #define M_ATTR_OFFSET_OVERFLOW \ _ ("seccomp_data load offset greater than the struct") #define M_ARGS_IDX_OUT_OF_RANGE _ ("Args index out of range (0-5)") #define M_MEM_IDX_OUT_OF_RANGE _ ("Mem index out of range (0-15)") #define M_UNINITIALIZED_MEM _ ("Accessing uninitialized mem") #define M_ALU_DIV_BY_ZERO _ ("Dividing by zero") #define M_ALU_SH_OUT_OF_RANGE _ ("Left or right shifting more than 32 bits") #define M_RET_DATA_OVERFLOW _ ("Data carried by return motion exceeds 0xffff") #define M_JT_TOO_FAR _ ("JT is larger than 0xff") #define M_JT_MUST_BE_POSITIVE _ ("JT must be positive") #define M_JF_TOO_FAR _ ("JF is larger than 0xff") #define M_JF_MUST_BE_POSITIVE _ ("JF must be positive") #define M_JT_INVALID_TAG _ ("JT out of filters") #define M_JF_INVALID_TAG _ ("JF out of filters") #define M_JA_OUT_OF_FILTERS _ ("JA out of filters") #define M_MUST_END_WITH_RET _ ("BPF filters must end with return") #define M_NO_VALID_CODE _ ("The input does not contain any valid statement") #define M_INVALID_OPERATION _ ("Invalid or unknown operation") #endif ceccomp-4.0/include/utils/hash.h000066400000000000000000000005461514205130000166410ustar00rootroot00000000000000#ifndef HASH_H #define HASH_H #include "main.h" #include #include typedef string_t hkey_t; // return 0 if successfully inserted; -1 for ENOMEM; 1 for duplicated key extern int insert_key (hkey_t *key, uint16_t line_nr); extern uint16_t find_key (hkey_t *key); extern void init_table (void); extern void free_table (void); #endif ceccomp-4.0/include/utils/logger.h000066400000000000000000000017261514205130000171760ustar00rootroot00000000000000#ifndef LOGGER_H #define LOGGER_H #include // clang-format off __attribute__ ((noinline)) void debug_print (const char *caller_func, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); __attribute__ ((noinline)) void info_print (const char *caller_func, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); __attribute__ ((noinline)) void warn_print (const char *caller_func, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); __attribute__ ((noinline)) __attribute__ ((noreturn)) void error_print (const char *caller_func, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); // clang-format on #ifdef DEBUG #define debug(fmt, ...) debug_print (__func__, fmt, __VA_ARGS__) #else #define debug(fmt, ...) ; #endif // !DEBUG #define info(fmt, ...) info_print (__func__, fmt, __VA_ARGS__) #define warn(fmt, ...) warn_print (__func__, fmt, __VA_ARGS__) #define error(fmt, ...) error_print (__func__, fmt, __VA_ARGS__) #endif ceccomp-4.0/include/utils/parse_args.h000066400000000000000000000024001514205130000200330ustar00rootroot00000000000000#ifndef PARSEARGS_H #define PARSEARGS_H #include #include #include #include typedef enum { ASM_MODE, DISASM_MODE, EMU_MODE, TRACE_MODE, PROBE_MODE, HELP_MODE, VERSION_MODE, HELP_ABNORMAL, } subcommand_t; typedef enum { HEXLINE, HEXFMT, RAW, } print_mode_t; typedef enum { ALWAYS, AUTO, NEVER, } color_mode_t; typedef struct { uint32_t scmp_arch; print_mode_t mode; FILE *text_file; } asm_arg_t; typedef struct { uint32_t scmp_arch; FILE *raw_file; } disasm_arg_t; typedef struct { uint32_t scmp_arch; bool quiet; FILE *text_file; const char *sys_name; uint64_t args[6]; uint64_t ip; } emu_arg_t; typedef struct { FILE *output_file; bool quiet; uint32_t prog_idx; } probe_arg_t; typedef enum { UNDECIDED, TRACE_PROG, TRACE_PID, } trace_mode_t; typedef struct { trace_mode_t mode; bool quiet; FILE *output_file; uint32_t prog_idx; pid_t pid; bool seize; } trace_arg_t; typedef struct { subcommand_t cmd; color_mode_t when; asm_arg_t *asm_arg; disasm_arg_t *disasm_arg; emu_arg_t *emu_arg; probe_arg_t *probe_arg; trace_arg_t *trace_arg; } ceccomp_arg_t; extern error_t parse_opt (int key, char *arg, struct argp_state *state); #endif ceccomp-4.0/include/utils/proc_status.h000066400000000000000000000006461514205130000202650ustar00rootroot00000000000000#ifndef PROCSTATUS_H #define PROCSTATUS_H #include #define PROCFS_ERROR -1 typedef enum { STATUS_NONE = 0, STATUS_STRICT_MODE = 1, STATUS_FILTER_MODE = 2, } seccomp_mode; typedef enum { STATUS_NOT_KTHREAD = 0, STATUS_KTHREAD = 1, } kthread_mode; extern seccomp_mode get_proc_seccomp (pid_t pid); extern kthread_mode is_proc_kthread (pid_t pid); extern pid_t get_tracer_pid (pid_t pid); #endif ceccomp-4.0/include/utils/read_source.h000066400000000000000000000004051514205130000202030ustar00rootroot00000000000000#ifndef READ_SOURCE_H #define READ_SOURCE_H #include // read file at once, the pointer is all fixed extern unsigned init_source (FILE *read_fp); // return NULL to indicate EOF extern char *next_line (void); extern void free_source (void); #endif ceccomp-4.0/include/utils/reverse_endian.h000066400000000000000000000007611514205130000207060ustar00rootroot00000000000000#ifndef REVERSE_ENDIAN_H #define REVERSE_ENDIAN_H #include "main.h" #include #include #include #include static inline bool need_reverse_endian (uint32_t target_scmp_arch) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return (target_scmp_arch & __AUDIT_ARCH_LE); #else return !(target_scmp_arch & __AUDIT_ARCH_LE); #endif } static inline void reverse_endian (filter *f) { f->code = bswap_16 (f->code); f->k = bswap_32 (f->k); } #endif ceccomp-4.0/include/utils/str_pile.h000066400000000000000000000011161514205130000175310ustar00rootroot00000000000000#ifndef STRING_PILE_H #define STRING_PILE_H #include "main.h" #include #include /** * Initialize string pile with init_size to mmap. init_size will be aligned up * to 0x1000. * Returns if mmap succeeded. */ extern bool init_pile (size_t init_size); /** * Slice some mmap memory to store syscall name. * sysname: syscall name string. NULL-terminated. * arch: a token from token_pairs. if not NULL, store "arch.sysname", else * "sysname". */ extern string_t persist_object (const char *sysname, const string_t *arch); extern void free_pile (void); #endif ceccomp-4.0/include/utils/vector.h000066400000000000000000000014031514205130000172110ustar00rootroot00000000000000#ifndef VECTOR_H #define VECTOR_H #include #include // A vector store statement ordered by code_nr // A vector stores statement_t ptr ordered by text_nr // code_nr and text_nr both starts from 1 // statement at index 0 should be ignored // eof_lines won't be in the vector typedef struct { uint32_t count; uint32_t capacity; size_t elem_size; void *data; } vector_t; extern void *reallocate (void *p, size_t new_size); // set initial_capcity to 0 to let vector grow itself extern void init_vector (vector_t *v, size_t elem_size, size_t initial_capcity); extern void free_vector (vector_t *v); extern void *push_vector (vector_t *v, void *elem); extern void *get_vector (vector_t *v, uint32_t idx); #endif ceccomp-4.0/po/000077500000000000000000000000001514205130000133735ustar00rootroot00000000000000ceccomp-4.0/po/Makefile000066400000000000000000000013031514205130000150300ustar00rootroot00000000000000GREEN := $(shell printf '\033[32m') RESET := $(shell printf '\033[0m') ECHO_NOPROG = printf " $(1)\t$(2)\n" INC_DIR := ../include SRC_DIR := ../src INC_SRCS := $(shell find $(INC_DIR) -name '*.h') C_SRCS := $(shell find $(SRC_DIR) -name '*.c' -or -name '*.s') SOURCES_LIST := SOURCES.txt POT_FILE := ceccomp.pot PO_ZHCN := zh_CN.po all: $(PO_ZHCN) $Q$(call ECHO_NOPROG,$(GREEN)UPDATED,po$(RESET)) $(SOURCES_LIST): $(INC_SRCS) $(C_SRCS) $Q$(call ECHO_NOPROG,TR,$@) $Qecho $^ | tr ' ' '\n' > $@ $(POT_FILE): $(SOURCES_LIST) $Q$(call ECHO_NOPROG,XGETTEXT,$@) $Qxgettext -k_ -o $@ -f $< $(PO_ZHCN): $(POT_FILE) $Q$(call ECHO_NOPROG,MSGMERGE,$@) $Qmsgmerge --previous --update $@ $< 2> /dev/null ceccomp-4.0/po/ceccomp.pot000066400000000000000000000237451514205130000155430ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-06 20:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../include/config.h:11 #, c-format msgid "" "ceccomp %s\n" "Tag at %s\n" "Build by %s\n" msgstr "" #: ../include/help.h:5 msgid "Usage: ceccomp \n" msgstr "" #: ../include/help.h:25 msgid "" "asm -- Assemble bpf text to raw bytes\n" "disasm -- Disassemble raw bytes to bpf text\n" "emu -- Emulate bpf program with given syscall and bpf text\n" "help -- Display ceccomp help information\n" "probe -- Trace the program for the first filter and emulate common " "syscalls\n" "trace -- Run program or trace pid, extract bpf filter and then print to " "text\n" "version -- Display ceccomp version\n" msgstr "" #: ../include/help.h:36 msgid "" "Options:\n" "-a, --arch (x86_64|aarch64|...) Which architecture to resolve syscall_nr, " "read or write bytestream by the byteorder of arch, default as your arch\n" "-f, --fmt (hexline|hexfmt|raw) Output format, default as hexline\n" "-p, --pid system_process_id Extract bpf filters from process and print " "with bpf text form; CAP_SYS_ADMIN is needed to work\n" "-o, --output file Print to file to avoid mixing ceccomp " "output and tracee program output, default as stderr\n" "-q, --quiet Print emulate result only or suppress the " "process info message in trace and probe\n" "-s, --seize Follow process to trace load-filter " "operation\n" "-c, --color (auto|always|never) When to print in color, default as auto\n" "syscall_nr System call number or name (e.g. 0|read)\n" "args[0-5], ip args and ip (instruction pointer) used for " "emulation, default as 0\n" "raw, text File with BPF RAW or BPF TEXT, see docs for " "detail, default as stdin\n" msgstr "" #: ../include/utils/error.h:7 msgid "Found errors when assembling BPF from trace" msgstr "" #: ../include/utils/error.h:8 #, c-format msgid "Failed to request a temporary file: %s" msgstr "" #: ../include/utils/error.h:12 msgid "Found errors when assembling text before emulating" msgstr "" #: ../include/utils/error.h:13 msgid "Please input syscall_nr (syscall name or number)" msgstr "" #: ../include/utils/error.h:14 msgid "Invalid syscall_nr" msgstr "" #: ../include/utils/error.h:17 msgid "Found errors when assembling" msgstr "" #: ../include/utils/error.h:18 msgid "Input file has more than 1024 statements!" msgstr "" #: ../include/utils/error.h:21 msgid "Found fatal errors when disassembling" msgstr "" #: ../include/utils/error.h:22 msgid "The input is empty" msgstr "" #: ../include/utils/error.h:24 msgid "The input is larger than 1024 filters! Perhaps inputting a wrong file?" msgstr "" #: ../include/utils/error.h:27 #, c-format msgid "%d byte(s) at the end of input could not fit into a filter" msgstr "" #: ../include/utils/error.h:30 #, c-format msgid "Start tracing process %d" msgstr "" #: ../include/utils/error.h:31 #, c-format msgid "Peekdata failed at %p" msgstr "" #: ../include/utils/error.h:32 msgid "Failed to execute new program" msgstr "" #: ../include/utils/error.h:33 #, c-format msgid "Process %d spawned a new pid %d" msgstr "" #: ../include/utils/error.h:34 #, c-format msgid "Process %d exited" msgstr "" #: ../include/utils/error.h:35 #, c-format msgid "Parsing seccomp filter loaded in process %d" msgstr "" #: ../include/utils/error.h:36 #, c-format msgid "Process %d failed to load seccomp filter" msgstr "" #: ../include/utils/error.h:37 msgid "Procfs not accessible, unable to perform" msgstr "" #: ../include/utils/error.h:38 msgid "Found unresolvable non-classic BPF, skipping" msgstr "" #: ../include/utils/error.h:39 msgid "Kernel thread can not be seized" msgstr "" #: ../include/utils/error.h:40 #, c-format msgid "No seccomp filters found in process %d" msgstr "" #: ../include/utils/error.h:42 #, c-format msgid "Target process is already being traced by process %d" msgstr "" #: ../include/utils/error.h:44 #, c-format msgid "" "Process %d loaded strict seccomp mode, which only allows read, write, " "exit_group and sigreturn!" msgstr "" #: ../include/utils/error.h:46 #, c-format msgid "Unknown error when get filter: %s" msgstr "" #: ../include/utils/error.h:47 #, c-format msgid "Unknown error when seizing process: %s" msgstr "" #: ../include/utils/error.h:48 msgid "The process to seize does not exist" msgstr "" #: ../include/utils/error.h:54 msgid "perhaps seizing kthread or lacking CAP_SYS_PTRACE" msgstr "" #: ../include/utils/error.h:56 msgid "Run with CAP_SYS_ADMIN capability to fetch seccomp filters" msgstr "" #: ../include/utils/error.h:58 msgid "Run with CAP_SYS_PTRACE capability to seize a foreign process" msgstr "" #: ../include/utils/error.h:60 msgid "32-bit tracer can not ptrace 64-bit tracee" msgstr "" #: ../include/utils/error.h:62 #, c-format msgid "libseccomp does not support the tracee's arch (%#x)" msgstr "" #: ../include/utils/error.h:64 msgid "perhaps lacking CAP_SYS_ADMIN or ceccomp is in seccomp mode" msgstr "" #: ../include/utils/error.h:66 msgid "PTRACE_GET_SECCOMP_FILTER is not supported on your system" msgstr "" #: ../include/utils/error.h:68 msgid "" "Ceccomp is in seccomp mode, fetching seccomp filters of other process is not " "permitted" msgstr "" #: ../include/utils/error.h:71 msgid "" "perhaps PTRACE_GET_SECCOMP_FILTER is not supported or no seccomp filter in " "target process" msgstr "" #: ../include/utils/error.h:75 msgid "Invalid color mode" msgstr "" #: ../include/utils/error.h:76 msgid "Invalid format mode" msgstr "" #: ../include/utils/error.h:77 msgid "Invalid number" msgstr "" #: ../include/utils/error.h:78 #, c-format msgid "Unable to open file %s: %s" msgstr "" #: ../include/utils/error.h:81 #, c-format msgid "Reading input failed: %s" msgstr "" #: ../include/utils/error.h:83 #, c-format msgid "Found '\\0' at file offset %lu, perhaps it's not a text file?" msgstr "" #: ../include/utils/error.h:85 msgid "No line break in source file, perhaps it's not a text file?" msgstr "" #: ../include/utils/error.h:87 #, c-format msgid "Line %u has more than %u bytes, perhaps the input is not a text file?" msgstr "" #: ../include/utils/error.h:88 msgid "The input file is greater than 1 MiB!" msgstr "" #: ../include/utils/error.h:90 msgid "Found more than 4096 lines of text, perhaps it's not for ceccomp?" msgstr "" #: ../include/utils/error.h:93 msgid "Can not find label declaration" msgstr "" #: ../include/utils/error.h:96 msgid "Unexpected token" msgstr "" #: ../include/utils/error.h:97 msgid "Found duplicated label declaration" msgstr "" #: ../include/utils/error.h:98 msgid "Input number exceeds 32-bit range" msgstr "" #: ../include/utils/error.h:100 msgid "Expect operator" msgstr "" #: ../include/utils/error.h:101 msgid "Expect rvalue" msgstr "" #: ../include/utils/error.h:102 msgid "Expect return value" msgstr "" #: ../include/utils/error.h:104 msgid "Expect number" msgstr "" #: ../include/utils/error.h:105 msgid "Expect parenthesis" msgstr "" #: ../include/utils/error.h:106 msgid "Expect bracket" msgstr "" #: ../include/utils/error.h:107 msgid "Expect comparator" msgstr "" #: ../include/utils/error.h:108 msgid "Expect label" msgstr "" #: ../include/utils/error.h:109 msgid "Expect valid syscall name" msgstr "" #: ../include/utils/error.h:111 msgid "" "Architecture token does not match any libseccomp arch, perhaps system " "libseccomp is too old" msgstr "" #: ../include/utils/error.h:115 msgid "Expect 'goto'" msgstr "" #: ../include/utils/error.h:116 msgid "Expect '$A'" msgstr "" #: ../include/utils/error.h:117 msgid "Expect 'else'" msgstr "" #: ../include/utils/error.h:120 msgid "Rvalue should be '$A'" msgstr "" #: ../include/utils/error.h:121 msgid "Rvalue can not be '$A'" msgstr "" #: ../include/utils/error.h:122 msgid "Rvalue can not be '$X'" msgstr "" #: ../include/utils/error.h:124 msgid "Rvalue should be '$A' or '$X'" msgstr "" #: ../include/utils/error.h:125 msgid "Rvalue should be '$X' or number" msgstr "" #: ../include/utils/error.h:126 msgid "Operator should be '='" msgstr "" #: ../include/utils/error.h:127 msgid "Lvalue should be '$A'" msgstr "" #: ../include/utils/error.h:129 msgid "seccomp_data load offset must be 4-byte aligned" msgstr "" #: ../include/utils/error.h:131 msgid "seccomp_data load offset greater than the struct" msgstr "" #: ../include/utils/error.h:133 msgid "Args index out of range (0-5)" msgstr "" #: ../include/utils/error.h:134 msgid "Mem index out of range (0-15)" msgstr "" #: ../include/utils/error.h:135 msgid "Accessing uninitialized mem" msgstr "" #: ../include/utils/error.h:136 msgid "Dividing by zero" msgstr "" #: ../include/utils/error.h:137 msgid "Left or right shifting more than 32 bits" msgstr "" #: ../include/utils/error.h:139 msgid "Data carried by return motion exceeds 0xffff" msgstr "" #: ../include/utils/error.h:140 msgid "JT is larger than 0xff" msgstr "" #: ../include/utils/error.h:141 msgid "JT must be positive" msgstr "" #: ../include/utils/error.h:142 msgid "JF is larger than 0xff" msgstr "" #: ../include/utils/error.h:143 msgid "JF must be positive" msgstr "" #: ../include/utils/error.h:144 msgid "JT out of filters" msgstr "" #: ../include/utils/error.h:145 msgid "JF out of filters" msgstr "" #: ../include/utils/error.h:146 msgid "JA out of filters" msgstr "" #: ../include/utils/error.h:148 msgid "BPF filters must end with return" msgstr "" #: ../include/utils/error.h:149 msgid "The input does not contain any valid statement" msgstr "" #: ../include/utils/error.h:151 msgid "Invalid or unknown operation" msgstr "" ceccomp-4.0/po/zh_CN.po000066400000000000000000000355151514205130000147450ustar00rootroot00000000000000# localization for ceccomp in Chinese simplified. # Copyright (C) 2025-present ceccomp contributors # This file is distributed under the same license as the ceccomp package. # SPDX-FileCopyrightText: 2025 dbgbgtf 1 # # SPDX-FileCopyrightText: 2025 RocketDev msgid "" msgstr "" "Project-Id-Version: ceccomp 4.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-06 20:03+0800\n" "PO-Revision-Date: 2026-02-06 20:04+0800\n" "Last-Translator: RocketDev \n" "Language-Team: Chinese \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.8\n" #: ../include/config.h:11 #, c-format msgid "" "ceccomp %s\n" "Tag at %s\n" "Build by %s\n" msgstr "" "ceccomp %s\n" "在 %s 发布\n" "由 %s 构建\n" #: ../include/help.h:5 msgid "Usage: ceccomp \n" msgstr "用法: ceccomp <子命令> <参数> <选项>\n" #: ../include/help.h:25 msgid "" "asm -- Assemble bpf text to raw bytes\n" "disasm -- Disassemble raw bytes to bpf text\n" "emu -- Emulate bpf program with given syscall and bpf text\n" "help -- Display ceccomp help information\n" "probe -- Trace the program for the first filter and emulate common " "syscalls\n" "trace -- Run program or trace pid, extract bpf filter and then print to " "text\n" "version -- Display ceccomp version\n" msgstr "" "asm -- 把 bpf text 编译为 raw bytes\n" "disasm -- 把 raw bytes 编译为 bpf text\n" "emu -- 在给定的 syscall 和 bpf text 下模拟 bpf 程序的结果\n" "help -- 显示帮助信息\n" "probe -- trace 程序,对程序中出现的第一个 filter 模拟常见的 syscall\n" "trace -- 运行程序或追踪指定 pid,提取 bpf 过滤器,并打印 text 结果\n" "version -- 显示 ceccomp 版本\n" #: ../include/help.h:36 msgid "" "Options:\n" "-a, --arch (x86_64|aarch64|...) Which architecture to resolve syscall_nr, " "read or write bytestream by the byteorder of arch, default as your arch\n" "-f, --fmt (hexline|hexfmt|raw) Output format, default as hexline\n" "-p, --pid system_process_id Extract bpf filters from process and print " "with bpf text form; CAP_SYS_ADMIN is needed to work\n" "-o, --output file Print to file to avoid mixing ceccomp " "output and tracee program output, default as stderr\n" "-q, --quiet Print emulate result only or suppress the " "process info message in trace and probe\n" "-s, --seize Follow process to trace load-filter " "operation\n" "-c, --color (auto|always|never) When to print in color, default as auto\n" "syscall_nr System call number or name (e.g. 0|read)\n" "args[0-5], ip args and ip (instruction pointer) used for " "emulation, default as 0\n" "raw, text File with BPF RAW or BPF TEXT, see docs for " "detail, default as stdin\n" msgstr "" "选项:\n" "-a, --arch (x86_64|aarch64|...) 选择哪一个架构来解析 syscall_nr,默认为你的" "架构\n" "-f, --fmt (hexline|hexfmt|raw) 输出格式, 默认为 hexline\n" "-p, --pid system_process_id 从进程中提取 bpf 过滤器,并打印为 text 格" "式; 需要 CAP_SYS_ADMIN 来运作\n" "-o, --output file 将结果打印到文件来避免混淆 ceccomp 输出和 " "tracee 程序输出,默认打印至 stderr\n" "-q, --quiet 只打印 emulate 结果或在 trace 和 probe 时抑" "制进程的 INFO 消息\n" "-s, --seize 追踪进程以分析加载过滤器的操作\n" "-c, --color (auto|always|never) 指定何时使用彩色输出, 默认为 auto\n" "syscall_nr 系统调用号或系统调用名 (示例 0|read)\n" "args[0-5], ip 用来模拟的系统调用的参数和 ip (instruction " "pointer),默认为 0\n" "raw, text raw 或 text 文件,默认为 stdin。更多细节请查" "阅文档\n" #: ../include/utils/error.h:7 msgid "Found errors when assembling BPF from trace" msgstr "当汇编从 trace 取得的 BPF 时发现错误" #: ../include/utils/error.h:8 #, c-format msgid "Failed to request a temporary file: %s" msgstr "无法申请临时文件:%s" #: ../include/utils/error.h:12 msgid "Found errors when assembling text before emulating" msgstr "在模拟前汇编 text 时发现错误" #: ../include/utils/error.h:13 msgid "Please input syscall_nr (syscall name or number)" msgstr "请输入 syscall_nr(syscall 名字或数字)" #: ../include/utils/error.h:14 msgid "Invalid syscall_nr" msgstr "无效的系统调用号" #: ../include/utils/error.h:17 msgid "Found errors when assembling" msgstr "汇编时发现错误" #: ../include/utils/error.h:18 msgid "Input file has more than 1024 statements!" msgstr "输入包含超过 1024 行伪代码!" #: ../include/utils/error.h:21 msgid "Found fatal errors when disassembling" msgstr "反汇编时发现致命错误" #: ../include/utils/error.h:22 msgid "The input is empty" msgstr "输入为空" #: ../include/utils/error.h:24 msgid "The input is larger than 1024 filters! Perhaps inputting a wrong file?" msgstr "输入大小超过了 1024 个过滤器的大小!可能输入了错误的文件?" #: ../include/utils/error.h:27 #, c-format msgid "%d byte(s) at the end of input could not fit into a filter" msgstr "输入末尾的 %d 个字节无法放入一个过滤器中" #: ../include/utils/error.h:30 #, c-format msgid "Start tracing process %d" msgstr "开始追踪进程 %d" #: ../include/utils/error.h:31 #, c-format msgid "Peekdata failed at %p" msgstr "无法在 %p peekdata" #: ../include/utils/error.h:32 msgid "Failed to execute new program" msgstr "无法执行新程序" #: ../include/utils/error.h:33 #, c-format msgid "Process %d spawned a new pid %d" msgstr "进程 %d 派生了新进程 %d" #: ../include/utils/error.h:34 #, c-format msgid "Process %d exited" msgstr "进程 %d 退出" #: ../include/utils/error.h:35 #, c-format msgid "Parsing seccomp filter loaded in process %d" msgstr "正在解析进程 %d 加载的 seccomp 过滤器" #: ../include/utils/error.h:36 #, c-format msgid "Process %d failed to load seccomp filter" msgstr "进程 %d 加载 seccomp 过滤器失败" #: ../include/utils/error.h:37 msgid "Procfs not accessible, unable to perform" msgstr "Procfs 无法访问,无法执行" #: ../include/utils/error.h:38 msgid "Found unresolvable non-classic BPF, skipping" msgstr "发现无法解析的非经典 BPF,跳过" #: ../include/utils/error.h:39 msgid "Kernel thread can not be seized" msgstr "内核线程不能被附加" #: ../include/utils/error.h:40 #, c-format msgid "No seccomp filters found in process %d" msgstr "在进程 %d 中没有找到 seccomp 过滤器" #: ../include/utils/error.h:42 #, c-format msgid "Target process is already being traced by process %d" msgstr "目标进程已经被进程 %d 追踪" #: ../include/utils/error.h:44 #, c-format msgid "" "Process %d loaded strict seccomp mode, which only allows read, write, " "exit_group and sigreturn!" msgstr "" "进程 %d 加载了严格 seccomp 模式,只有 read, write, exit_group 和 sigreturn 被" "允许!" #: ../include/utils/error.h:46 #, c-format msgid "Unknown error when get filter: %s" msgstr "在 get filter 时发生未知错误:%s" #: ../include/utils/error.h:47 #, c-format msgid "Unknown error when seizing process: %s" msgstr "在附加到进程时发生未知错误:%s" #: ../include/utils/error.h:48 msgid "The process to seize does not exist" msgstr "要附加的进程不存在" #: ../include/utils/error.h:54 msgid "perhaps seizing kthread or lacking CAP_SYS_PTRACE" msgstr "可能正在附加到内核线程或缺少 CAP_SYS_PTRACE 权能" #: ../include/utils/error.h:56 msgid "Run with CAP_SYS_ADMIN capability to fetch seccomp filters" msgstr "以 CAP_SYS_ADMIN 权能运行来获取 seccomp 过滤器" #: ../include/utils/error.h:58 msgid "Run with CAP_SYS_PTRACE capability to seize a foreign process" msgstr "以 CAP_SYS_PTRACE 权能运行来附加到一个外部进程" #: ../include/utils/error.h:60 msgid "32-bit tracer can not ptrace 64-bit tracee" msgstr "32 位 tracer 无法 ptrace 64 位 tracee" #: ../include/utils/error.h:62 #, c-format msgid "libseccomp does not support the tracee's arch (%#x)" msgstr "libseccomp 不支持 tracee 的架构 (%#x)" #: ../include/utils/error.h:64 msgid "perhaps lacking CAP_SYS_ADMIN or ceccomp is in seccomp mode" msgstr "可能缺少 CAP_SYS_ADMIN 权能或 ceccomp 处于 seccomp 模式" #: ../include/utils/error.h:66 msgid "PTRACE_GET_SECCOMP_FILTER is not supported on your system" msgstr "PTRACE_SECCOMP_GET_FILTER 在您的系统上不支持" #: ../include/utils/error.h:68 msgid "" "Ceccomp is in seccomp mode, fetching seccomp filters of other process is not " "permitted" msgstr "Ceccomp 处在 seccomp 模式,不允许获取其他进程的 seccomp 过滤器" #: ../include/utils/error.h:71 msgid "" "perhaps PTRACE_GET_SECCOMP_FILTER is not supported or no seccomp filter in " "target process" msgstr "" "可能 PTRACE_SECCOMP_GET_FILTER 在您的系统上不支持或目标进程没有 seccomp 过滤" "器" #: ../include/utils/error.h:75 msgid "Invalid color mode" msgstr "无效的颜色模式" #: ../include/utils/error.h:76 msgid "Invalid format mode" msgstr "无效的 asm 格式参数" #: ../include/utils/error.h:77 msgid "Invalid number" msgstr "无效的数字" #: ../include/utils/error.h:78 #, c-format msgid "Unable to open file %s: %s" msgstr "无法打开文件 %s: %s" #: ../include/utils/error.h:81 #, c-format msgid "Reading input failed: %s" msgstr "读取输入失败:%s" #: ../include/utils/error.h:83 #, c-format msgid "Found '\\0' at file offset %lu, perhaps it's not a text file?" msgstr "在文件偏移 %lu 处发现 '\\0',可能不是一个文本文件?" #: ../include/utils/error.h:85 msgid "No line break in source file, perhaps it's not a text file?" msgstr "没有在源文件中找到换行符,可能不是一个文本文件?" #: ../include/utils/error.h:87 #, c-format msgid "Line %u has more than %u bytes, perhaps the input is not a text file?" msgstr "第 %u 行上有超过 %u 个字节,可能输入不是一个文本文件?" #: ../include/utils/error.h:88 msgid "The input file is greater than 1 MiB!" msgstr "输入的文件超过了 1 MiB!" #: ../include/utils/error.h:90 msgid "Found more than 4096 lines of text, perhaps it's not for ceccomp?" msgstr "文件中包含超过 4096 行,可能它不是给 ceccomp 用的?" #: ../include/utils/error.h:93 msgid "Can not find label declaration" msgstr "无法找到标签声明" #: ../include/utils/error.h:96 msgid "Unexpected token" msgstr "意料之外的 token" #: ../include/utils/error.h:97 msgid "Found duplicated label declaration" msgstr "发现重复的标签声明" #: ../include/utils/error.h:98 msgid "Input number exceeds 32-bit range" msgstr "输入的数字超过了 32 位的范围" #: ../include/utils/error.h:100 msgid "Expect operator" msgstr "期待一个操作符" #: ../include/utils/error.h:101 msgid "Expect rvalue" msgstr "期待一个右值" #: ../include/utils/error.h:102 msgid "Expect return value" msgstr "期待一个 return 值" #: ../include/utils/error.h:104 msgid "Expect number" msgstr "期待一个数字" #: ../include/utils/error.h:105 msgid "Expect parenthesis" msgstr "期待一个小括号" #: ../include/utils/error.h:106 msgid "Expect bracket" msgstr "期待一个中括号" #: ../include/utils/error.h:107 msgid "Expect comparator" msgstr "期待一个比较符" #: ../include/utils/error.h:108 msgid "Expect label" msgstr "期待一个标签" #: ../include/utils/error.h:109 msgid "Expect valid syscall name" msgstr "期待一个系统调用名称" #: ../include/utils/error.h:111 msgid "" "Architecture token does not match any libseccomp arch, perhaps system " "libseccomp is too old" msgstr "" "架构 token 没有匹配到任何 libseccomp 支持的架构,可能是因为系统 libseccomp 太" "老了" #: ../include/utils/error.h:115 msgid "Expect 'goto'" msgstr "期待一个 'goto'" #: ../include/utils/error.h:116 msgid "Expect '$A'" msgstr "期待一个 '$A'" #: ../include/utils/error.h:117 msgid "Expect 'else'" msgstr "期待一个 'else'" #: ../include/utils/error.h:120 msgid "Rvalue should be '$A'" msgstr "右值应该为 '$A'" #: ../include/utils/error.h:121 msgid "Rvalue can not be '$A'" msgstr "右值不能为 '$A'" #: ../include/utils/error.h:122 msgid "Rvalue can not be '$X'" msgstr "右值不能为 '$X'" #: ../include/utils/error.h:124 msgid "Rvalue should be '$A' or '$X'" msgstr "右值应该为 '$A' 或 '$X'" #: ../include/utils/error.h:125 msgid "Rvalue should be '$X' or number" msgstr "右值应该为 '$X' 或数字" #: ../include/utils/error.h:126 msgid "Operator should be '='" msgstr "操作符应该为 '='" #: ../include/utils/error.h:127 msgid "Lvalue should be '$A'" msgstr "左值应该为 '$A'" #: ../include/utils/error.h:129 msgid "seccomp_data load offset must be 4-byte aligned" msgstr "seccomp_data 加载偏移必须对齐到 4 字节" #: ../include/utils/error.h:131 msgid "seccomp_data load offset greater than the struct" msgstr "seccomp_data 加载偏移超过了结构体本身的大小" #: ../include/utils/error.h:133 msgid "Args index out of range (0-5)" msgstr "访问 args 索引超出范围 (0-5)" #: ../include/utils/error.h:134 msgid "Mem index out of range (0-15)" msgstr "访问 mem 索引超出范围 (0-5)" #: ../include/utils/error.h:135 msgid "Accessing uninitialized mem" msgstr "尝试访问未初始化的 mem" #: ../include/utils/error.h:136 msgid "Dividing by zero" msgstr "除以零" #: ../include/utils/error.h:137 msgid "Left or right shifting more than 32 bits" msgstr "左移或右移超过 32 位" #: ../include/utils/error.h:139 msgid "Data carried by return motion exceeds 0xffff" msgstr "return 动作携带的 data 超过了 0xffff" #: ../include/utils/error.h:140 msgid "JT is larger than 0xff" msgstr "JT 大于了 0xff" #: ../include/utils/error.h:141 msgid "JT must be positive" msgstr "JT 必须是正数" #: ../include/utils/error.h:142 msgid "JF is larger than 0xff" msgstr "JF 大于了 0xff" #: ../include/utils/error.h:143 msgid "JF must be positive" msgstr "JF 必须是正数" #: ../include/utils/error.h:144 msgid "JT out of filters" msgstr "JT 超出 BPF 长度" #: ../include/utils/error.h:145 msgid "JF out of filters" msgstr "JF 超出 BPF 长度" #: ../include/utils/error.h:146 msgid "JA out of filters" msgstr "跳转结果超出 BPF 长度" #: ../include/utils/error.h:148 msgid "BPF filters must end with return" msgstr "BPF 过滤器必须以 return 结尾" #: ../include/utils/error.h:149 msgid "The input does not contain any valid statement" msgstr "输入不包含任何有效的伪代码" #: ../include/utils/error.h:151 msgid "Invalid or unknown operation" msgstr "无效或未知的操作符" ceccomp-4.0/py-temp/000077500000000000000000000000001514205130000143505ustar00rootroot00000000000000ceccomp-4.0/py-temp/ceccomp/000077500000000000000000000000001514205130000157615ustar00rootroot00000000000000ceccomp-4.0/py-temp/ceccomp/__init__.py000066400000000000000000000004271514205130000200750ustar00rootroot00000000000000import os import sys from pathlib import Path import stat def main(): bin_path = Path(__file__).parent / "ceccomp" os.chmod(bin_path, os.stat(bin_path).st_mode | stat.S_IEXEC) os.execv(bin_path, [str(bin_path)] + sys.argv[1:]) if __name__ == "__main__": main() ceccomp-4.0/py-temp/pyproject.toml000066400000000000000000000006671514205130000172750ustar00rootroot00000000000000[build-system] requires = ["setuptools>=69"] build-backend = "setuptools.build_meta" [project] name = "ceccomp" version = "3.5" description = "A C-based seccomp analysis tool" authors = [{name = "dbgbgtf", email = "dudududumaxver@outlook.com"}] license = "GPL-3.0-or-later" scripts = { "ceccomp" = "ceccomp:main"} [tool.setuptools] packages = ["ceccomp"] include-package-data = true [tool.setuptools.package-data] ceccomp = ["ceccomp"] ceccomp-4.0/scripts/000077500000000000000000000000001514205130000144445ustar00rootroot00000000000000ceccomp-4.0/scripts/check-version.hook000077500000000000000000000020231514205130000200660ustar00rootroot00000000000000#!/bin/bash COMMIT_MSG_FILE="$1" if [[ $COMMIT_MSG_FILE == "install" ]]; then ln -sf ../../scripts/$(basename $0) .git/hooks/commit-msg echo "commit-msg hook installed" exit 0 fi VERSION_REGEX='\b[0-9]+\.[0-9]+\b' print_highlight() { echo -e "\x1b[33m$1\x1b[0m" >&2 } if grep -qE "$VERSION_REGEX" "$COMMIT_MSG_FILE"; then print_highlight "========================================" print_highlight "Version update detected!" print_highlight "Please check if updated configure" print_highlight "========================================" print_highlight python3 scripts/complete-url.py --check > /dev/null action_req=$? if [ $action_req -eq 1 ]; then print_highlight "========================================" print_highlight "There are raw reference to issues/pulls in CHANGELOG!" print_highlight "Please run scripts/complete-url.py to format" print_highlight "========================================" print_highlight exit 1 fi fi exit 0 ceccomp-4.0/scripts/complete-url.py000066400000000000000000000056641514205130000174410ustar00rootroot00000000000000#!/usr/bin/python3 import re import sys import os CHANGELOG = 'CHANGELOG.md' DEFAULT_REPO = 'dbgbgtf1/Ceccomp' # match 3 raw ref case:#123, !456, user/repo#789, user/repo!456 pattern = re.compile(r'(?:\s+)((?:[\w.-]+/[\w.-]+)?[#!]\d+)$') # match '@username ' user_pat = re.compile(r'@([a-zA-Z0-9_-]+) ') def convert(token: str) -> str: """Convert raw ref to URL""" repo = DEFAULT_REPO if '#' in token: if '/' in token.split('#')[0]: repo, number = token.split('#', 1) else: number = token[1:] return f'https://github.com/{repo}/issues/{number}' if '!' in token: if '/' in token.split('!')[0]: repo, number = token.split('!', 1) else: number = token[1:] return f'https://github.com/{repo}/pulls/{number}' assert False, f'Token {token} does not match any case, what the hack?' def flush_refs(output_lines: list[str], refs: dict[str, str], need_lf: bool): """Write refs at the end of paragraph""" if refs: if need_lf: output_lines.append('\n') # terminate list output_lines.extend(f'[{token}]: {url}\n' for token, url in refs.items()) refs.clear() def main(): if not os.path.isdir('.git'): print('Please goto repo root first', file=sys.stderr) sys.exit(2) is_check = len(sys.argv) > 1 and sys.argv[1] == '--check' has_match = False with open(CHANGELOG) as f: lines = f.readlines() refs = {} # token:url output_lines = [] has_old_ref = False for lineno, line in enumerate(lines, start=1): if line.startswith('##'): flush_refs(output_lines, refs, not has_old_ref) has_old_ref = False elif line.startswith('['): has_old_ref = True # look for @user in line, NOT AT LINE END! mu = user_pat.search(line) if mu: user = mu.group(1) url = f'https://github.com/{user}' refs[f'@{user}'] = url line = line.replace(mu.group(0), f':heart: [@{user}] ') if is_check: print(f'[line {lineno}] found: {user}') has_match = True m = pattern.search(line) if m: token = m.group(1) url = convert(token) refs[token] = url new_line = line[: m.start(1)].rstrip() + f' :link: [{token}]\n' if is_check: print(f'[line {lineno}] found: {token}') has_match = True else: output_lines.append(new_line) elif not is_check: output_lines.append(line) # flush at EOF flush_refs(output_lines, refs, not has_old_ref) if not is_check: with open(CHANGELOG, 'w') as f: f.writelines(output_lines) # set exit code to 1 when see convertible ref in check mode if is_check and has_match: sys.exit(1) if __name__ == '__main__': main() ceccomp-4.0/scripts/compress-images.sh000077500000000000000000000002201514205130000200730ustar00rootroot00000000000000#!/bin/sh magick mogrify -format webp -resize 70% -quality 80 -define webp:method=6 -alpha remove -define webp:lossless=false docs/images/*.png ceccomp-4.0/scripts/find-unused-macros.sh000077500000000000000000000017171514205130000205140ustar00rootroot00000000000000#!/bin/bash macros=($(grep -P '(?<=^#define )[A-Z0-9_]+' -ohr include --exclude-dir=lib/ | sort | uniq)) echo "Collected ${#macros[@]} macros from include" not_found=() for macro in "${macros[@]}"; do grep -E "\b$macro\b" -r src &>/dev/null if [ $? -ne 0 ]; then not_found+=("$macro") fi done echo "Found $((${#macros[@]} - ${#not_found[@]})) macro reference in src" if [ ${#not_found[@]} -eq 0 ]; then echo "All macros found" exit 0 fi unused=() for macro in "${not_found[@]}"; do cnt=$(grep -E "\b$macro\b" -ohr include --exclude-dir=lib/ 2>/dev/null | wc -l) if [ $cnt -eq 1 ]; then unused+=("$macro") fi done echo "Found $((${#not_found[@]} - ${#unused[@]})) macro reference in include" if [ ${#unused[@]} -eq 0 ]; then echo "All macros found" exit 0 fi echo "Found ${#unused[@]} macros unused!" for macro in "${unused[@]}"; do grep -E "\b$macro\b" -nr include --exclude-dir=lib/ --color=auto done exit 1 ceccomp-4.0/scripts/release-changelog.py000066400000000000000000000022601514205130000203630ustar00rootroot00000000000000#!/usr/bin/python3 import sys import re CHANGELOG = 'CHANGELOG.md' if len(sys.argv) < 2: print(f'Usage: python {sys.argv[0]} ', file=sys.stderr) sys.exit(1) tag = sys.argv[1] if not re.match(r'v\d+\.\d+(\.\d+)?', tag): print(f'The tag {tag} does not match the rule vX.X or vX.X.X', file=sys.stderr) sys.exit(1) version = tag[1:] print(f'Locating version {version} in {CHANGELOG}', file=sys.stderr) pattern_version = re.compile(rf'^##\s*{re.escape(version)}\s*') pattern_header = re.compile(r'^##\s*') lines = None try: with open(CHANGELOG) as f: lines = f.readlines() except OSError as e: print(f'Failed to read file {CHANGELOG}: {e}') sys.exit(1) capturing = False for line in lines: if not capturing: # mark if found the target version section if pattern_version.match(line.strip()): capturing = True else: # print the whole line if the line is not next version section if pattern_header.match(line.strip()): break print(line, end='') # already containing a '\n' if not capturing: # no match print(f'No match version section for {version}', file=sys.stderr) sys.exit(1) ceccomp-4.0/scripts/update-bpf.sh000077500000000000000000000012341514205130000170320ustar00rootroot00000000000000#!/bin/bash override_bpf() { ./build/ceccomp asm -c always -f raw $1 > $2 2>&1 } # for txt in test/text/*; do # bpf=${txt/text/bpf} # bpf=${bpf}.bpf # override_bpf $txt $bpf # done override_text() { ./build/ceccomp disasm -c always $1 > $2 2>&1 } for bpf in test/bpf/*; do txt=${bpf%.bpf} txt=${txt/bpf/text} override_text $bpf $txt done override_emu() { ./build/ceccomp emu -c always $1 $2 1 2 3 4 5 6 > $3 2>&1 } for text in test/text/*; do emu_result=${text/text/emu_result} override_emu $text open $emu_result.open override_emu $text pipe $emu_result.pipe override_emu $text accept $emu_result.accept done ceccomp-4.0/scripts/upload-pypi.sh000077500000000000000000000030311514205130000172430ustar00rootroot00000000000000#!/bin/bash set -e # 通用构建函数 build_and_upload() { BUILD_TARGET=$1 WHEEL_TAG=$2 echo "===== 构建架构:($BUILD_TARGET) =====" ./configure \ --debug-level=0 \ --enable-static \ --without-i18n \ --without-doc \ --packager=pip \ --build=${BUILD_TARGET} make clean make mv build/ceccomp py-temp/ceccomp/ cd py-temp python -m build --wheel oldfile=$(ls dist/ceccomp-*-py3-none-any.whl | head -n 1) newfile="${oldfile/any/${WHEEL_TAG}}" mv "$oldfile" "$newfile" twine upload "$newfile" --repository ceccomp rm -rf dist/ build/ ceccomp.egg-info/ wheelhouse/ ceccomp/ceccomp cd .. } build_x86_64() { build_and_upload "x86_64-linux-gnu" "manylinux1_x86_64" } build_i386() { build_and_upload "i386-linux-gnu" "manylinux1_i686" } build_aarch64() { build_and_upload "aarch64-linux-gnu" "manylinux2014_aarch64" } build_armhf() { build_and_upload "arm-linux-gnueabihf" "manylinux2014_armv7l" } build_riscv64() { build_and_upload "riscv64-linux-gnu" "manylinux_2_31_riscv64" } # 根据参数选择执行 case "$1" in x86_64) build_x86_64;; i386) build_i386;; aarch64) build_aarch64;; armhf) build_armhf;; riscv64) build_riscv64;; all) build_x86_64 build_i386 build_aarch64 build_armhf build_riscv64 ;; *) echo "用法: $0 {x86_64|i386|aarch64|armhf|riscv64|all}" exit 1 ;; esac ceccomp-4.0/src/000077500000000000000000000000001514205130000135445ustar00rootroot00000000000000ceccomp-4.0/src/asm.c000066400000000000000000000222461514205130000144760ustar00rootroot00000000000000#include "asm.h" #include "lexical/parser.h" #include "lexical/scanner.h" #include "lexical/token.h" #include "main.h" #include "resolver/resolver.h" #include "utils/error.h" #include "utils/logger.h" #include "utils/parse_args.h" #include "utils/read_source.h" #include "utils/reverse_endian.h" #include "utils/vector.h" #include #include #include #include #include #include #include #include #include #define BASE_retvals(value) ((value) - KILL_PROC) static uint32_t retvals[] = { [BASE_retvals (KILL_PROC)] = SCMP_ACT_KILL_PROCESS, [BASE_retvals (KILL)] = SCMP_ACT_KILL, [BASE_retvals (ALLOW)] = SCMP_ACT_ALLOW, [BASE_retvals (NOTIFY)] = SCMP_ACT_NOTIFY, [BASE_retvals (LOG)] = SCMP_ACT_LOG, [BASE_retvals (TRACE)] = SCMP_ACT_TRACE (0), [BASE_retvals (TRAP)] = _SCMP_ACT_TRAP (0), [BASE_retvals (ERRNO)] = SCMP_ACT_ERRNO (0), }; static filter return_line (return_line_t *return_line) { filter f = { .code = BPF_RET, .jf = 0, .jt = 0, .k = 0 }; if (return_line->ret_obj.type == A) { f.code |= BPF_A; return f; } f.code |= BPF_K; if (return_line->ret_obj.type == NUMBER) f.k = return_line->ret_obj.data; else // KILL, KILL_PROC, ALLOW, NOTIFY, LOG has data = 0 f.k = retvals[BASE_retvals (return_line->ret_obj.type)] | return_line->ret_obj.data; return f; } #undef BASE_retvals #define BASE_operator(value) ((value) - ADD_TO) static const uint32_t operator_table[] = { [BASE_operator (ADD_TO)] = BPF_ADD, [BASE_operator (SUB_TO)] = BPF_SUB, [BASE_operator (MULTI_TO)] = BPF_MUL, [BASE_operator (DIVIDE_TO)] = BPF_DIV, [BASE_operator (LSH_TO)] = BPF_LSH, [BASE_operator (RSH_TO)] = BPF_RSH, [BASE_operator (AND_TO)] = BPF_AND, [BASE_operator (OR_TO)] = BPF_OR, [BASE_operator (XOR_TO)] = BPF_XOR, }; static filter alu_line (assign_line_t *assign_line) { filter f = { .code = BPF_ALU, .jf = 0, .jt = 0, .k = 0 }; f.code |= operator_table[BASE_operator (assign_line->operator)]; if (assign_line->right_var.type == X) f.code |= BPF_X; else if (assign_line->right_var.type == NUMBER) { f.code |= BPF_K; f.k = assign_line->right_var.data; } return f; } #undef BASE_operator static filter negative_line (void) { filter f = { .code = BPF_ALU | BPF_NEG, .jf = 0, .jt = 0, .k = 0 }; return f; } static filter st_stx_line (assign_line_t *assign_line) { filter f = { .code = 0, .jf = 0, .jt = 0, .k = 0 }; f.k = assign_line->left_var.data; if (assign_line->right_var.type == A) f.code |= BPF_ST; else if (assign_line->right_var.type == A) f.code |= BPF_STX; return f; } static filter ldx_line (assign_line_t *assign_line) { filter f = { .code = 0, .jf = 0, .jt = 0, .k = 0 }; if (assign_line->right_var.type == A) { f.code |= BPF_MISC | BPF_TAX; return f; } if (assign_line->right_var.type == NUMBER) f.code |= BPF_LDX | BPF_IMM; else if (assign_line->right_var.type == MEM) f.code |= BPF_LDX | BPF_MEM; else if (assign_line->right_var.type == ATTR_LEN) f.code |= BPF_LDX | BPF_LEN; else assert (!"right_var is neither NUM, MEM nor ATTR_LEN"); f.k = assign_line->right_var.data; return f; } #define BASE_off(value) ((value) - ATTR_SYSCALL) static const uint32_t offset_table[] = { [BASE_off (ATTR_SYSCALL)] = offsetof (seccomp_data, nr), [BASE_off (ATTR_ARCH)] = offsetof (seccomp_data, arch), [BASE_off (ATTR_LOWPC)] = offsetof (seccomp_data, instruction_pointer), [BASE_off (ATTR_HIGHPC)] = offsetof (seccomp_data, instruction_pointer) + 4, [BASE_off (ATTR_LOWARG)] = offsetof (seccomp_data, args), [BASE_off (ATTR_HIGHARG)] = offsetof (seccomp_data, args) + 4, }; static uint32_t offset_abs (obj_t *obj) { uint32_t offset = offset_table[BASE_off (obj->type)]; if (obj->type == ATTR_LOWARG || obj->type == ATTR_HIGHARG) offset += obj->data * sizeof (uint64_t); return offset; } #undef BASE_off static filter ld_line (assign_line_t *assign_line) { filter f = { .code = 0, .jf = 0, .jt = 0, .k = 0 }; if (assign_line->right_var.type == X) f.code |= BPF_MISC | BPF_TXA; else if (assign_line->right_var.type == ATTR_LEN) f.code |= BPF_LD | BPF_LEN; else if (assign_line->right_var.type == NUMBER) { f.code |= BPF_LD | BPF_IMM; f.k = assign_line->right_var.data; } else if (assign_line->right_var.type == MEM) { f.code |= BPF_LD | BPF_MEM; f.k = assign_line->right_var.data; } else { f.code |= BPF_LD | BPF_W | BPF_ABS; f.k = offset_abs (&assign_line->right_var); } return f; } static filter assign_line (assign_line_t *assign_line) { if (assign_line->operator >= ADD_TO && assign_line->operator <= XOR_TO) return alu_line (assign_line); if (assign_line->operator == NEGATIVE) return negative_line (); if (assign_line->left_var.type == MEM) return st_stx_line (assign_line); if (assign_line->left_var.type == X) return ldx_line (assign_line); assert (assign_line->left_var.type == A); return ld_line (assign_line); } static void reverse_jf_jt (jump_line_t *jump_line) { label_t tmp = jump_line->jt; jump_line->jt = jump_line->jf; jump_line->jf = tmp; } #define BASE_comparator(value) ((value) - EQUAL_EQUAL) static const uint32_t comparator_table[] = { [BASE_comparator (EQUAL_EQUAL)] = BPF_JEQ, [BASE_comparator (BANG_EQUAL)] = BPF_JEQ, [BASE_comparator (GREATER_EQUAL)] = BPF_JGE, [BASE_comparator (LESS_THAN)] = BPF_JGE, [BASE_comparator (GREATER_THAN)] = BPF_JGT, [BASE_comparator (LESS_EQUAL)] = BPF_JGT, [BASE_comparator (AND)] = BPF_JSET, }; static filter jump_line (jump_line_t *jump_line) { filter f = { .code = BPF_JMP, .jf = 0, .jt = 0, .k = 0 }; if (!jump_line->if_condition) { f.code |= BPF_JA; f.k |= jump_line->jt.code_nr; return f; } bool sym_reverse = false; token_type comparator = jump_line->comparator; if (comparator == BANG_EQUAL || comparator == LESS_EQUAL || comparator == LESS_THAN) sym_reverse = true; f.code |= comparator_table[BASE_comparator (jump_line->comparator)]; if (sym_reverse ^ jump_line->if_bang) reverse_jf_jt (jump_line); f.jt = jump_line->jt.code_nr; f.jf = jump_line->jf.code_nr; if (jump_line->cmpobj.type == X) f.code |= BPF_X; else if (jump_line->cmpobj.type == NUMBER) { f.code |= BPF_K; f.k = jump_line->cmpobj.data; } return f; } #undef BASE_comparator static filter asm_statement (statement_t *statement) { if (statement->type == RETURN_LINE) return return_line (&statement->return_line); if (statement->type == ASSIGN_LINE) return assign_line (&statement->assign_line); if (statement->type == JUMP_LINE) return jump_line (&statement->jump_line); assert (0); } typedef void (*fmt_handler) (uint8_t f[8], char *template); static void hexify (uint8_t ch, char buf[2]) { #define HEX_CODE "0123456789abcdef" buf[0] = HEX_CODE[ch >> 4]; buf[1] = HEX_CODE[ch & 0xf]; } #define HEXFMT_TEMPLATE "\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",\n" #define HEXFMT_TEMPLATE_LEN LITERAL_STRLEN (HEXFMT_TEMPLATE) static void hexfmt_handle (uint8_t f[8], char *template) { for (unsigned i = 0; i < 8; i++) hexify (f[i], template + 3 + 4 * i); fwrite (template, 1, HEXFMT_TEMPLATE_LEN, stdout); } #define HEXLINE_TEMPLATE "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00" #define HEXLINE_TEMPLATE_LEN LITERAL_STRLEN (HEXLINE_TEMPLATE) static void hexline_handle (uint8_t f[8], char *template) { for (unsigned i = 0; i < 8; i++) hexify (f[i], template + 2 + 4 * i); fwrite (template, 1, HEXLINE_TEMPLATE_LEN, stdout); } static void raw_handle (uint8_t f[8], char *template) { (void)template; fwrite (f, 1, 8, stdout); } static fmt_handler set_print_fmt (print_mode_t print_mode, char *template) { switch (print_mode) { case HEXFMT: memcpy (template, HEXFMT_TEMPLATE, HEXFMT_TEMPLATE_LEN + 1); return hexfmt_handle; case HEXLINE: memcpy (template, HEXLINE_TEMPLATE, HEXLINE_TEMPLATE_LEN + 1); return hexline_handle; case RAW: *template = '\0'; // for debugging, don't hurt performance return raw_handle; default: assert (!"Unknown fmt printer"); } } void assemble (FILE *fp, uint32_t scmp_arch, print_mode_t print_mode) { size_t lines = init_source (fp) + 1; init_scanner (next_line ()); init_parser (scmp_arch); init_table (); vector_t text_v; vector_t code_ptr_v; init_vector (&text_v, sizeof (statement_t), lines); init_vector (&code_ptr_v, sizeof (statement_t *), MIN (lines, 1025)); parser (&text_v, &code_ptr_v); if (resolver (&code_ptr_v)) error ("%s", M_ASM_TERMINATED); // if ERROR_LINE exists, then exits char fmt_template[64]; fmt_handler handle = set_print_fmt (print_mode, fmt_template); bool reverse = need_reverse_endian (scmp_arch); for (uint32_t i = 1; i < code_ptr_v.count; i++) { statement_t **ptr = get_vector (&code_ptr_v, i); filter f = asm_statement (*ptr); if (reverse) reverse_endian (&f); handle ((uint8_t *)&f, fmt_template); } if (print_mode == HEXLINE) putc ('\n', stdout); free_table (); free_source (); free_vector (&text_v); free_vector (&code_ptr_v); } ceccomp-4.0/src/ceccomp.c000066400000000000000000000111671514205130000153270ustar00rootroot00000000000000#include "asm.h" #include "config.h" #include "disasm.h" #include "emu.h" #include "probe.h" #include "trace.h" #include "utils/arch_trans.h" #include "utils/color.h" #include "utils/parse_args.h" #include #include #include #include #include #include #include #include #include static struct utsname uts; static asm_arg_t asm_arg; static disasm_arg_t disasm_arg; static emu_arg_t emu_arg; static probe_arg_t probe_arg; static trace_arg_t trace_arg; static ceccomp_arg_t args = { .cmd = HELP_ABNORMAL, .asm_arg = &asm_arg, .disasm_arg = &disasm_arg, .emu_arg = &emu_arg, .probe_arg = &probe_arg, .trace_arg = &trace_arg }; static const struct argp_option options[] = { { "quiet", 'q', NULL, 0, NULL, 0 }, { "color", 'c', "COLOR", 0, NULL, 0 }, { "output", 'o', "OUTPUT", 0, NULL, 0 }, { "arch", 'a', "ARCH", 0, NULL, 0 }, { "pid", 'p', "PID", 0, NULL, 0 }, { "fmt", 'f', "FMT", 0, NULL, 0 }, { "seize", 's', NULL, 0, NULL, 0 }, { "help", 'h', NULL, 0, NULL, 0 }, { "usage", 'u', NULL, 0, NULL, 0 }, { 0 }, }; static void init_args (ceccomp_arg_t *args) { uname (&uts); uint32_t scmp_arch = str_to_scmp_arch (uts.machine); args->asm_arg->scmp_arch = scmp_arch; args->asm_arg->mode = HEXLINE; args->asm_arg->text_file = stdin; args->disasm_arg->scmp_arch = scmp_arch; args->disasm_arg->raw_file = stdin; args->emu_arg->scmp_arch = scmp_arch; args->emu_arg->text_file = stdin; for (uint32_t i = 0; i <= 5; i++) args->emu_arg->args[i] = 0; args->emu_arg->ip = 0; args->emu_arg->quiet = false; args->emu_arg->sys_name = NULL; args->probe_arg->output_file = stderr; args->probe_arg->prog_idx = 0; args->trace_arg->mode = UNDECIDED; args->trace_arg->output_file = stderr; args->trace_arg->pid = 0; args->trace_arg->prog_idx = 0; args->trace_arg->quiet = false; args->trace_arg->seize = false; const char *no_color = getenv ("NO_COLOR"); if (no_color != NULL && no_color[0] != '\0') args->when = NEVER; else args->when = AUTO; } __attribute__ ((noreturn)) static void help (int exit_code) { printf ("%s", M_CECCOMP_USAGE); putchar ('\n'); printf ("%s\n", ASM_HINT); printf ("%s\n", DISASM_HINT); printf ("%s\n", EMU_HINT); printf ("%s\n", PROBE_HINT); printf ("%s\n", TRACE_HINT); printf ("%s\n", HELP_HINT); printf ("%s\n", VERSION_HINT); printf ("\n%s\n", M_SUBCMD_HINT); printf ("\n%s\n", M_OPTION_HINT); exit (exit_code); } __attribute__ ((noreturn)) static void version (void) { printf (M_VERSION_FORMAT, CECCOMP_VERSION, CECCOMP_TAG_TIME, CECCOMP_BUILDER); exit (0); } int main (int argc, char *argv[]) { lc_c = newlocale (LC_ALL_MASK, "C", NULL); #ifdef LOCALEDIR setlocale (LC_ALL, ""); bindtextdomain ("ceccomp", LOCALEDIR); textdomain ("ceccomp"); #endif #ifdef DEBUG setbuf (stdin, NULL); setbuf (stdout, NULL); setbuf (stderr, NULL); #else setvbuf (stderr, NULL, _IOLBF, BUFSIZ); #endif init_args (&args); static struct argp argp = { options, parse_opt, NULL, NULL, NULL, NULL, NULL }; argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, &args); if (args.cmd == TRACE_MODE && trace_arg.mode == TRACE_PROG) set_color (args.when, trace_arg.output_file); else if (args.cmd == PROBE_MODE) set_color (args.when, probe_arg.output_file); else set_color (args.when, stdout); switch (args.cmd) { case ASM_MODE: set_color (args.when, stdout); assemble (asm_arg.text_file, asm_arg.scmp_arch, asm_arg.mode); break; case DISASM_MODE: set_color (args.when, stdout); disasm (disasm_arg.raw_file, disasm_arg.scmp_arch); break; case EMU_MODE: set_color (args.when, stdout); emulate (&emu_arg); break; case TRACE_MODE: if (trace_arg.mode == TRACE_PID) { set_color (args.when, stdout); pid_trace (trace_arg.pid, trace_arg.seize, trace_arg.quiet); } else if (trace_arg.mode == TRACE_PROG) { program_trace (&argv[trace_arg.prog_idx], trace_arg.output_file, trace_arg.quiet, false); } break; case PROBE_MODE: probe (&argv[probe_arg.prog_idx], probe_arg.output_file, probe_arg.quiet); break; case HELP_MODE: help (0); break; case HELP_ABNORMAL: help (1); break; case VERSION_MODE: version (); break; default: assert (!"unknown mode"); } } ceccomp-4.0/src/decoder/000077500000000000000000000000001514205130000151515ustar00rootroot00000000000000ceccomp-4.0/src/decoder/check_prog.c000066400000000000000000000163421514205130000174270ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-or-later /** * This part of code are adapted from Linux kernel, * source files are: net/core/filter.c and kernel/seccomp.c * * Thanks to following authors and their commit time: * * 2012 Will Drewry * 2004-2005 Andrea Arcangeli * 2011-2014 PLUMgrid, http://plumgrid.com * Jay Schulist * Alexei Starovoitov * Daniel Borkmann * Andi Kleen * Kris Katterjohn */ #include "decoder/check_prog.h" #include "main.h" #include "utils/error.h" #include "utils/logger.h" #include "utils/vector.h" #include #include #include #include #include #include #include static const bool codes[] = { /* 32 bit ALU operations */ [BPF_ALU | BPF_ADD | BPF_K] = true, [BPF_ALU | BPF_ADD | BPF_X] = true, [BPF_ALU | BPF_SUB | BPF_K] = true, [BPF_ALU | BPF_SUB | BPF_X] = true, [BPF_ALU | BPF_MUL | BPF_K] = true, [BPF_ALU | BPF_MUL | BPF_X] = true, [BPF_ALU | BPF_DIV | BPF_K] = true, [BPF_ALU | BPF_DIV | BPF_X] = true, [BPF_ALU | BPF_AND | BPF_K] = true, [BPF_ALU | BPF_AND | BPF_X] = true, [BPF_ALU | BPF_OR | BPF_K] = true, [BPF_ALU | BPF_OR | BPF_X] = true, [BPF_ALU | BPF_XOR | BPF_K] = true, [BPF_ALU | BPF_XOR | BPF_X] = true, [BPF_ALU | BPF_LSH | BPF_K] = true, [BPF_ALU | BPF_LSH | BPF_X] = true, [BPF_ALU | BPF_RSH | BPF_K] = true, [BPF_ALU | BPF_RSH | BPF_X] = true, [BPF_ALU | BPF_NEG] = true, /* Load instructions */ [BPF_LD | BPF_W | BPF_ABS] = true, [BPF_LD | BPF_W | BPF_LEN] = true, [BPF_LD | BPF_IMM] = true, [BPF_LD | BPF_MEM] = true, [BPF_LDX | BPF_W | BPF_LEN] = true, [BPF_LDX | BPF_IMM] = true, [BPF_LDX | BPF_MEM] = true, /* Store instructions */ [BPF_ST] = true, [BPF_STX] = true, /* Misc instructions */ [BPF_MISC | BPF_TAX] = true, [BPF_MISC | BPF_TXA] = true, /* Return instructions */ [BPF_RET | BPF_K] = true, [BPF_RET | BPF_A] = true, /* Jump instructions */ [BPF_JMP | BPF_JA] = true, [BPF_JMP | BPF_JEQ | BPF_K] = true, [BPF_JMP | BPF_JEQ | BPF_X] = true, [BPF_JMP | BPF_JGE | BPF_K] = true, [BPF_JMP | BPF_JGE | BPF_X] = true, [BPF_JMP | BPF_JGT | BPF_K] = true, [BPF_JMP | BPF_JGT | BPF_X] = true, [BPF_JMP | BPF_JSET | BPF_K] = true, [BPF_JMP | BPF_JSET | BPF_X] = true, }; static uint16_t *masks, mem_valid = 0; static uint32_t fatal_count = 0; static uint32_t error_count = 0; typedef enum { CODE_ERR, JT_ERR, JF_ERR, K_ERR, NONE, } err_idx; uint32_t err_len[] = { [CODE_ERR] = 0, [JT_ERR] = LITERAL_STRLEN ("CODE:0x1234 "), [JF_ERR] = LITERAL_STRLEN ("CODE:0x1234 JT:0x56"), [K_ERR] = LITERAL_STRLEN ("CODE:0x1234 JF:0x56 JT:0x78 "), [NONE] = LITERAL_STRLEN ("CODE:0x1234 JF:0x56 JF:0x78 K:0x9abcdef0"), }; #define SPRINTF_CAT(...) print += sprintf (print, __VA_ARGS__) static void report_error (filter f, uint32_t filter_idx, err_idx idx, bool fatal, const char *err_msg) { if (fatal) fatal_count++; else error_count++; char buf[0x400]; char *print = buf; SPRINTF_CAT ("#%d: %s\n", filter_idx, err_msg); SPRINTF_CAT ("CODE:0x%04x JT:0x%02x JF:0x%02x K:0x%08x\n", f.code, f.jt, f.jf, f.k); if (idx == NONE) { memset (print, '~', err_len[NONE]); print[err_len[idx]] = '\0'; } else { memset (print, ' ', err_len[idx]); print[err_len[idx]] = '^'; print[err_len[idx] + 1] = '\0'; } warn ("%s", buf); } #define REPORT(err_idx, fatal, err_msg) \ do \ { \ report_error (f, pc + 1, err_idx, fatal, err_msg); \ return; \ } \ while (0) #define FATAL true // return true means stop check static void check_filter (filter *fptr, uint32_t pc, uint32_t flen) { filter f = fptr[pc]; uint16_t code = f.code; uint32_t k = f.k; if (code >= ARRAY_SIZE (codes) || !codes[code]) REPORT (NONE, FATAL, M_INVALID_OPERATION); switch (code) { case BPF_LD | BPF_W | BPF_ABS: f.code = BPF_LDX | BPF_W | BPF_ABS; if (k >= sizeof (struct seccomp_data)) REPORT (K_ERR, FATAL, M_ATTR_OFFSET_OVERFLOW); else if (k & 3) REPORT (K_ERR, FATAL, M_INVALID_ATTR_LOAD); return; case BPF_LD | BPF_W | BPF_LEN: f.code = BPF_LD | BPF_IMM; f.k = sizeof (struct seccomp_data); return; case BPF_LDX | BPF_W | BPF_LEN: f.code = BPF_LDX | BPF_IMM; f.k = sizeof (struct seccomp_data); return; case BPF_ALU | BPF_DIV | BPF_K: if (f.k == 0) REPORT (K_ERR, !FATAL, M_ALU_DIV_BY_ZERO); return; case BPF_ALU | BPF_LSH | BPF_K: case BPF_ALU | BPF_RSH | BPF_K: if (f.k >= 32) REPORT (K_ERR, !FATAL, M_ALU_SH_OUT_OF_RANGE); return; case BPF_LD | BPF_MEM: case BPF_LDX | BPF_MEM: if (f.k >= BPF_MEMWORDS) REPORT (K_ERR, !FATAL, M_MEM_IDX_OUT_OF_RANGE); if (!(mem_valid & (1 << fptr[pc].k))) REPORT (K_ERR, !FATAL, M_UNINITIALIZED_MEM); return; case BPF_ST: case BPF_STX: if (f.k >= BPF_MEMWORDS) REPORT (K_ERR, !FATAL, M_MEM_IDX_OUT_OF_RANGE); mem_valid |= (1 << fptr[pc].k); return; case BPF_JMP | BPF_JA: if (f.k >= (uint32_t)(flen - pc - 1)) REPORT (K_ERR, !FATAL, M_JA_OUT_OF_FILTERS); masks[pc + 1 + fptr[pc].k] &= mem_valid; mem_valid = ~0; return; case BPF_JMP | BPF_JEQ | BPF_K: case BPF_JMP | BPF_JEQ | BPF_X: case BPF_JMP | BPF_JGE | BPF_K: case BPF_JMP | BPF_JGE | BPF_X: case BPF_JMP | BPF_JGT | BPF_K: case BPF_JMP | BPF_JGT | BPF_X: case BPF_JMP | BPF_JSET | BPF_K: case BPF_JMP | BPF_JSET | BPF_X: if (pc + f.jt + 1 >= flen) REPORT (JT_ERR, !FATAL, M_JT_INVALID_TAG); if (pc + f.jf + 1 >= flen) REPORT (JF_ERR, !FATAL, M_JF_INVALID_TAG); masks[pc + 1 + fptr[pc].jt] &= mem_valid; masks[pc + 1 + fptr[pc].jf] &= mem_valid; mem_valid = ~0; return; } } bool check_prog (fprog *prog) { masks = reallocate (NULL, sizeof (*masks) * prog->len); memset (masks, 0xff, sizeof (*masks) * prog->len); mem_valid = 0; for (uint16_t i = 0; i < prog->len; i++) { check_filter (prog->filter, i, prog->len); // if fatal_count > 5, stop disasm immediately // perhaps a wrong file? if (fatal_count > 5) goto complete; } filter last = prog->filter[prog->len - 1]; uint16_t last_code = last.code; if ((last_code != (BPF_RET | BPF_A)) && (last_code != (BPF_RET | BPF_K))) { report_error (last, prog->len, NONE, !FATAL, M_MUST_END_WITH_RET); goto complete; } complete: reallocate (masks, 0x0); // if fatal_error occurs, stop disasm if (fatal_count) error ("%s", M_DISASM_TERMINATED); // some error might cause mem overflow in render // if error_count != 0, skip render return (bool)error_count; } ceccomp-4.0/src/decoder/decoder.c000066400000000000000000000171521514205130000167300ustar00rootroot00000000000000#include "decoder/decoder.h" #include "decoder/check_prog.h" #include "lexical/parser.h" #include "lexical/token.h" #include "main.h" #include "resolver/resolver.h" #include "utils/vector.h" #include #include #include #include #include #include #include #include #include #define RSH2(value) ((value) >> 2) #define RSH4(value) ((value) >> 4) static const token_type abs_table[] = { [RSH2 (offsetof (seccomp_data, nr))] = ATTR_SYSCALL, [RSH2 (offsetof (seccomp_data, arch))] = ATTR_ARCH, [RSH2 (offsetof (seccomp_data, instruction_pointer))] = ATTR_LOWPC, [RSH2 (offsetof (seccomp_data, instruction_pointer) + 4)] = ATTR_HIGHPC, [RSH2 (offsetof (seccomp_data, args[0]))] = ATTR_LOWARG, [RSH2 (offsetof (seccomp_data, args[0]) + 4)] = ATTR_HIGHARG, [RSH2 (offsetof (seccomp_data, args[1]))] = ATTR_LOWARG, [RSH2 (offsetof (seccomp_data, args[1]) + 4)] = ATTR_HIGHARG, [RSH2 (offsetof (seccomp_data, args[2]))] = ATTR_LOWARG, [RSH2 (offsetof (seccomp_data, args[2]) + 4)] = ATTR_HIGHARG, [RSH2 (offsetof (seccomp_data, args[3]))] = ATTR_LOWARG, [RSH2 (offsetof (seccomp_data, args[3]) + 4)] = ATTR_HIGHARG, [RSH2 (offsetof (seccomp_data, args[4]))] = ATTR_LOWARG, [RSH2 (offsetof (seccomp_data, args[4]) + 4)] = ATTR_HIGHARG, [RSH2 (offsetof (seccomp_data, args[5]))] = ATTR_LOWARG, [RSH2 (offsetof (seccomp_data, args[5]) + 4)] = ATTR_HIGHARG, }; #define SCMP_DATA_LEN_STR "# 0x40" static void ld_ldx_line (filter f, statement_t *statement) { statement->type = ASSIGN_LINE; assign_line_t *assign_line = &statement->assign_line; assign_line->left_var.type = (BPF_CLASS (f.code) == BPF_LD) ? A : X; assign_line->operator = EQUAL; obj_t *right = &assign_line->right_var; switch (BPF_MODE (f.code)) { case BPF_IMM: right->type = NUMBER; right->data = f.k; return; case BPF_MEM: right->type = MEM; right->data = f.k; return; case BPF_ABS: right->type = abs_table[RSH2 (f.k)]; if (right->type == ATTR_LOWARG || right->type == ATTR_HIGHARG) right->data = (f.k - offsetof (seccomp_data, args[0])) / 0x8; return; case BPF_LEN: right->type = ATTR_LEN; statement->line_start = SCMP_DATA_LEN_STR; statement->comment = 0; statement->line_len = LITERAL_STRLEN (SCMP_DATA_LEN_STR); static_assert (sizeof (seccomp_data) == 0x40, "struct seccomp_data should be 0x40 bytes large"); return; default: assert (!"Unknown BPF_MODE for ld or ldx"); } } static void st_stx_line (filter f, statement_t *statement) { statement->type = ASSIGN_LINE; assign_line_t *assign_line = &statement->assign_line; assign_line->left_var.type = MEM; assign_line->left_var.data = f.k; assign_line->operator = EQUAL; assign_line->right_var.type = (BPF_CLASS (f.code) == BPF_ST) ? A : X; } static const token_type operator_table[] = { [RSH4 (BPF_ADD)] = ADD_TO, [RSH4 (BPF_SUB)] = SUB_TO, [RSH4 (BPF_MUL)] = MULTI_TO, [RSH4 (BPF_DIV)] = DIVIDE_TO, [RSH4 (BPF_OR)] = OR_TO, [RSH4 (BPF_AND)] = AND_TO, [RSH4 (BPF_LSH)] = LSH_TO, [RSH4 (BPF_RSH)] = RSH_TO, [RSH4 (BPF_NEG)] = NEGATIVE, [RSH4 (BPF_XOR)] = XOR_TO, }; static void alu_line (filter f, statement_t *statement) { statement->type = ASSIGN_LINE; assign_line_t *assign_line = &statement->assign_line; assign_line->left_var.type = A; assign_line->operator = operator_table[RSH4 (BPF_OP (f.code))]; if (BPF_SRC (f.code) == BPF_X) assign_line->right_var.type = X; else if (assign_line->operator == NEGATIVE) assign_line->right_var.type = A; else { assign_line->right_var.type = NUMBER; assign_line->right_var.data = f.k; } } static const token_type comparator_table[] = { [RSH4 (BPF_JEQ)] = EQUAL_EQUAL, [RSH4 (BPF_JGT)] = GREATER_THAN, [RSH4 (BPF_JGE)] = GREATER_EQUAL, [RSH4 (BPF_JSET)] = AND, }; static const token_type reverse_table[] = { [RSH4 (BPF_JEQ)] = BANG_EQUAL, [RSH4 (BPF_JGT)] = LESS_EQUAL, [RSH4 (BPF_JGE)] = LESS_THAN, }; static void condition (filter f, token_type *comparator, obj_t *cmpobj, bool *if_bang) { uint32_t op = BPF_OP (f.code); if ((!*if_bang) || op == BPF_JSET) *comparator = comparator_table[RSH4 (op)]; else { *comparator = reverse_table[RSH4 (op)]; *if_bang = false; } if (BPF_SRC (f.code) == BPF_X) cmpobj->type = X; else { cmpobj->type = NUMBER; cmpobj->data = f.k; } } static void jump_line (filter f, statement_t *statement) { statement->type = JUMP_LINE; jump_line_t *jump_line = &statement->jump_line; if (BPF_OP (f.code) == BPF_JA) { jump_line->if_bang = false; jump_line->if_condition = false; jump_line->jt.code_nr = f.k; return; } jump_line->if_condition = true; if (f.jt == 0 && f.jf != 0) { jump_line->if_bang = true; jump_line->jf.code_nr = f.jt; jump_line->jt.code_nr = f.jf; } else { jump_line->jt.code_nr = f.jt; jump_line->jf.code_nr = f.jf; } condition (f, &jump_line->comparator, &jump_line->cmpobj, &jump_line->if_bang); } token_type decode_return_k (obj_t *ret_obj, uint32_t k) { token_type tk; switch (k & SECCOMP_RET_ACTION_FULL) { case SCMP_ACT_KILL_PROCESS: tk = KILL_PROC; break; case SCMP_ACT_KILL: tk = KILL; break; case SCMP_ACT_ALLOW: tk = ALLOW; break; case SCMP_ACT_LOG: tk = LOG; break; case SCMP_ACT_TRACE (0): ret_obj->data = k & SECCOMP_RET_DATA; tk = TRACE; break; case _SCMP_ACT_TRAP (0): ret_obj->data = k & SECCOMP_RET_DATA; tk = TRAP; break; case SCMP_ACT_ERRNO (0): ret_obj->data = k & SECCOMP_RET_DATA; tk = ERRNO; break; default: ret_obj->data = k; tk = NUMBER; } ret_obj->type = tk; return tk == NUMBER ? KILL_PROC : tk; } static void return_line (filter f, statement_t *statement) { statement->type = RETURN_LINE; return_line_t *return_line = &statement->return_line; if (BPF_RVAL (f.code) == BPF_A) return_line->ret_obj.type = A; else decode_return_k (&return_line->ret_obj, f.k); } static void misc_line (filter f, statement_t *statement) { statement->type = ASSIGN_LINE; assign_line_t *assign_line = &statement->assign_line; assign_line->operator = EQUAL; if (BPF_MISCOP (f.code) == BPF_TAX) { assign_line->left_var.type = X; assign_line->right_var.type = A; } else { assign_line->left_var.type = A; assign_line->right_var.type = X; } } static void decode_filter (filter f, statement_t *statement) { switch (BPF_CLASS (f.code)) { case BPF_LD: case BPF_LDX: ld_ldx_line (f, statement); return; case BPF_ST: case BPF_STX: st_stx_line (f, statement); return; case BPF_ALU: alu_line (f, statement); break; case BPF_JMP: jump_line (f, statement); break; case BPF_RET: return_line (f, statement); break; case BPF_MISC: misc_line (f, statement); break; } } bool decode_filters (fprog *prog, vector_t *v) { // make sure all filter are valid // give warning about fatal and normal errors bool error = check_prog (prog); statement_t statement; push_vector (v, &statement); for (uint32_t i = 0; i < prog->len; i++) { memset (&statement, '\0', sizeof (statement_t)); statement.text_nr = i + 1; statement.code_nr = i + 1; decode_filter (prog->filter[i], &statement); push_vector (v, &statement); } return error; } ceccomp-4.0/src/decoder/formatter.c000066400000000000000000000131351514205130000173230ustar00rootroot00000000000000#include "decoder/formatter.h" #include "lexical/parser.h" #include "lexical/token.h" #include "main.h" #include "utils/color.h" #include #include #include #include #include FILE *fp; static void print_num (obj_t *tk) { if (tk->literal.start != NULL) fwrite (tk->literal.start, 1, tk->literal.len, fp); else fprintf (fp, "0x%x", tk->data); } static void print_str (obj_t *tk) { fwrite (token_pairs[tk->type].start, 1, token_pairs[tk->type].len, fp); } static void print_identifier (obj_t *tk) { fwrite (tk->literal.start, 1, tk->literal.len, fp); } static void print_dec_bracket (obj_t *tk) { fwrite (token_pairs[tk->type].start, 1, token_pairs[tk->type].len, fp); fprintf (fp, "[%d]", tk->data); } static void print_hex_bracket (obj_t *tk) { fwrite (token_pairs[tk->type].start, 1, token_pairs[tk->type].len, fp); fprintf (fp, "[0x%x]", tk->data); } static void print_paren (obj_t *tk) { fwrite (token_pairs[tk->type].start, 1, token_pairs[tk->type].len, fp); fprintf (fp, "(%d)", tk->data); } static const obj_print_t obj_print[] = { [A] = { print_str, BRIGHT_YELLOWCLR }, [X] = { print_str, BRIGHT_YELLOWCLR }, [MEM] = { print_hex_bracket, BRIGHT_YELLOWCLR }, [ATTR_LOWARG] = { print_dec_bracket, BRIGHT_BLUECLR }, [ATTR_HIGHARG] = { print_dec_bracket, BRIGHT_BLUECLR }, [ATTR_SYSCALL] = { print_str, BRIGHT_BLUECLR }, [ATTR_ARCH] = { print_str, BRIGHT_BLUECLR }, [ATTR_LOWPC] = { print_str, BRIGHT_BLUECLR }, [ATTR_HIGHPC] = { print_str, BRIGHT_BLUECLR }, [ATTR_LEN] = { print_str, BRIGHT_BLUECLR }, [IDENTIFIER] = { print_identifier, BRIGHT_CYANCLR }, [NUMBER] = { print_num, BRIGHT_CYANCLR }, [KILL_PROC] = { print_str, REDCLR }, [KILL] = { print_str, REDCLR }, [ALLOW] = { print_str, GREENCLR }, [NOTIFY] = { print_str, YELLOWCLR }, [LOG] = { print_str, YELLOWCLR }, [TRACE] = { print_paren, YELLOWCLR }, [TRAP] = { print_paren, YELLOWCLR }, [ERRNO] = { print_paren, REDCLR }, }; static void obj_printer (obj_t *obj) { if (color_enable) fprintf (fp, "%s", obj_print[obj->type].color); obj_print[obj->type].handler (obj); if (color_enable) fprintf (fp, "%s", CLR); } void extern_obj_printer (FILE *output_fp, obj_t *obj) { fp = output_fp; obj_printer (obj); } static inline void print_token_pair (token_type type) { fwrite (token_pairs[type].start, 1, token_pairs[type].len, fp); } static void assign_line (statement_t *statement) { assign_line_t *assign_line = &statement->assign_line; obj_t *left = &assign_line->left_var; obj_t *right = &assign_line->right_var; obj_printer (left); fputc (' ', fp); if (assign_line->operator == NEGATIVE) fprintf (fp, "= -"); else { print_token_pair (assign_line->operator); fputc (' ', fp); } obj_printer (right); } static inline void print_label (label_t *label, uint16_t pc) { if (label->key.start == NULL) fprintf (fp, DEFAULT_LABEL, pc + label->code_nr + 1); else fwrite (label->key.start, 1, label->key.len, fp); } static void print_ja (statement_t *statement) { print_token_pair (GOTO); fputc (' ', fp); print_label (&statement->jump_line.jt, statement->code_nr); } static void jump_line (statement_t *statement) { jump_line_t *jump_line = &statement->jump_line; if (!jump_line->if_condition) { print_ja (statement); return; } print_token_pair (IF); fputc (' ', fp); if (jump_line->if_bang) print_token_pair (BANG); print_token_pair (LEFT_PAREN); obj_t obj_A = { .type = A, .data = 0 }; obj_printer (&obj_A); fputc (' ', fp); print_token_pair (jump_line->comparator); fputc (' ', fp); obj_printer (&jump_line->cmpobj); print_token_pair (RIGHT_PAREN); fputc (' ', fp); print_token_pair (GOTO); fputc (' ', fp); print_label (&jump_line->jt, statement->code_nr); if (jump_line->jf.code_nr == 0) return; print_token_pair (COMMA); fputc (' ', fp); print_token_pair (ELSE); fputc (' ', fp); print_token_pair (GOTO); fputc (' ', fp); print_label (&jump_line->jf, statement->code_nr); } static void return_line (statement_t *statement) { print_token_pair (RETURN); fputc (' ', fp); obj_printer (&statement->return_line.ret_obj); } static void print_comment (statement_t *statement) { if (statement->comment == -1) return; char *comment_start = statement->line_start + statement->comment; uint16_t comment_len = statement->line_len - statement->comment; if (comment_len == 0) return; if (statement->type != EMPTY_LINE) fputc (' ', fp); // prepend a ' ' if a effective line (return 1 # aa) if (color_enable) fwrite (LIGHTCLR, 1, LITERAL_STRLEN (LIGHTCLR), fp); fwrite (comment_start, 1, comment_len, fp); if (color_enable) fwrite (CLR, 1, LITERAL_STRLEN (CLR), fp); } void print_as_comment (FILE *output_fp, const char *comment_fmt, ...) { fp = output_fp; va_list args; va_start (args, comment_fmt); char buf[0x400]; buf[0] = '#'; statement_t statement = { .type = EMPTY_LINE, .line_start = buf, .comment = 0 }; statement.line_len = vsnprintf (buf + 1, 0x3ff, comment_fmt, args) + 1; print_comment (&statement); fputc ('\n', fp); va_end (args); } void print_statement (FILE *output_fp, statement_t *statement) { fp = output_fp; switch (statement->type) { case ASSIGN_LINE: assign_line (statement); break; case JUMP_LINE: jump_line (statement); break; case RETURN_LINE: return_line (statement); break; case EMPTY_LINE: break; case ERROR_LINE: case EOF_LINE: assert (0); } print_comment (statement); fputc ('\n', fp); } ceccomp-4.0/src/disasm.c000066400000000000000000000045221514205130000151730ustar00rootroot00000000000000#include "disasm.h" #include "decoder/decoder.h" #include "decoder/formatter.h" #include "lexical/parser.h" #include "main.h" #include "resolver/render.h" #include "utils/error.h" #include "utils/logger.h" #include "utils/reverse_endian.h" #include "utils/str_pile.h" #include "utils/vector.h" #include #include #include #include #include #include #include filter g_filters[1025]; static uint32_t read_filters (filter *filters, FILE *from) { uint32_t todo = sizeof (filter) * (1024 + 1); uint8_t *ptr = (uint8_t *)filters; int fd = fileno (from); assert (fd != -1); while (todo) { long rc = read (fd, ptr, todo); if (rc == -1) error (M_READ_FAIL, strerror (errno)); if (rc == 0) break; ptr += rc; todo -= rc; } if (!todo) error ("%s", M_TOO_LARGE_INPUT); uint32_t leftover = (size_t)ptr & 7; if (leftover) warn (M_INPUT_HAS_LEFTOVER, leftover); return (ptr - (uint8_t *)filters) >> 3; } void print_prog (uint32_t scmp_arch, fprog *prog, FILE *output_fp) { if (need_reverse_endian (scmp_arch)) for (uint32_t i = 0; i < prog->len; i++) reverse_endian (&prog->filter[i]); vector_t v; // str pile for syscall names init_pile (prog->len * 40 /* statistical choice */); init_vector (&v, sizeof (statement_t), prog->len + 1); // check_prog in decode_filters might decect some errors // render might mem overflow, so skip render if (!decode_filters (prog, &v)) render (&v, scmp_arch); print_as_comment (output_fp, "Label CODE JT JF K"); print_as_comment (output_fp, "---------------------------------"); filter *filters = prog->filter; // give compiler some hint for (uint32_t i = 1; i < v.count; i++) { filter f = filters[i - 1]; fprintf (output_fp, " " DEFAULT_LABEL ": 0x%02x 0x%02x 0x%02x 0x%08x ", i, f.code, f.jt, f.jf, f.k); print_statement (output_fp, get_vector (&v, i)); } print_as_comment (output_fp, "---------------------------------"); free_vector (&v); free_pile (); } void disasm (FILE *fp, uint32_t scmp_arch) { fprog prog; prog.filter = g_filters; prog.len = read_filters (g_filters, fp); if (prog.len == 0) { warn ("%s", M_NO_FILTER); return; } print_prog (scmp_arch, &prog, stdout); } ceccomp-4.0/src/emu.c000066400000000000000000000231541514205130000145030ustar00rootroot00000000000000#include "emu.h" #include "decoder/decoder.h" #include "decoder/formatter.h" #include "lexical/parser.h" #include "lexical/scanner.h" #include "lexical/token.h" #include "main.h" #include "resolver/resolver.h" #include "utils/color.h" #include "utils/error.h" #include "utils/logger.h" #include "utils/parse_args.h" #include "utils/read_source.h" #include "utils/vector.h" #include #include #include #include #include #include #include #include #include static uint32_t A_reg = 0; static uint32_t X_reg = 0; static uint32_t mem[BPF_MEMWORDS] = { 0 }; static uint32_t syscall_nr = 0; static uint32_t scmp_arch = 0; static uint32_t low_pc = 0; static uint32_t high_pc = 0; static uint32_t low_args[6] = { 0 }; static uint32_t high_args[6] = { 0 }; static uint32_t scmp_data_len = sizeof (seccomp_data); #define BASE_vars(value) ((value) - A) static uint32_t *vars[] = { [BASE_vars (A)] = &A_reg, [BASE_vars (X)] = &X_reg, [BASE_vars (MEM)] = mem, [BASE_vars (ATTR_SYSCALL)] = &syscall_nr, [BASE_vars (ATTR_ARCH)] = &scmp_arch, [BASE_vars (ATTR_LOWPC)] = &low_pc, [BASE_vars (ATTR_HIGHPC)] = &high_pc, [BASE_vars (ATTR_LOWARG)] = low_args, [BASE_vars (ATTR_HIGHARG)] = high_args, [BASE_vars (ATTR_LEN)] = &scmp_data_len, }; #define DO_OPERATE(operator) (*left operator (*right)) static void assign_line (assign_line_t *assign_line) { uint32_t *left = vars[BASE_vars (assign_line->left_var.type)]; token_type right_type = assign_line->right_var.type; uint32_t *right = vars[BASE_vars (right_type)]; if (right_type == ATTR_LOWARG || right_type == ATTR_HIGHARG) right += assign_line->right_var.data; if (right_type == NUMBER) right = &assign_line->right_var.data; switch (assign_line->operator) { case ADD_TO: DO_OPERATE (+=); break; case SUB_TO: DO_OPERATE (-=); break; case MULTI_TO: DO_OPERATE (*=); break; case DIVIDE_TO: DO_OPERATE (/=); break; case LSH_TO: DO_OPERATE (<<=); break; case RSH_TO: DO_OPERATE (>>=); break; case AND_TO: DO_OPERATE (&=); break; case OR_TO: DO_OPERATE (|=); break; case XOR_TO: DO_OPERATE (^=); break; case EQUAL: DO_OPERATE (=); break; case NEGATIVE: DO_OPERATE (= -); break; default: assert (!"Unknown alu operation"); } } #define DO_COMPARE(comparator) (bool)(A_reg comparator right) static label_t * jump_line (jump_line_t *jump_line) { label_t *jt = &jump_line->jt; label_t *jf = &jump_line->jf; if (!jump_line->if_condition) return jt; bool cond_true = false; if (jump_line->if_bang) cond_true = true; uint32_t right = 0; if (jump_line->cmpobj.type == X) right = X_reg; else right = jump_line->cmpobj.data; switch (jump_line->comparator) { case EQUAL_EQUAL: cond_true ^= DO_COMPARE (==); break; case BANG_EQUAL: cond_true ^= DO_COMPARE (!=); break; case GREATER_EQUAL: cond_true ^= DO_COMPARE (>=); break; case GREATER_THAN: cond_true ^= DO_COMPARE (>); break; case LESS_EQUAL: cond_true ^= DO_COMPARE (<=); break; case LESS_THAN: cond_true ^= DO_COMPARE (<); break; case AND: cond_true ^= DO_COMPARE (&); break; default: assert (!"Unknown comparator"); } return (cond_true ? jt : jf); } /** * Process return line if return $A or return NUMBER. Sets real_ret to * resolved plain result like KILL; returns a valid string if statement * need to add a comment. */ static string_t return_line (return_line_t *line, obj_t *real_ret) { static char formatted_val[0x28]; obj_t *ret_obj = &line->ret_obj; *real_ret = (obj_t){ .type = UNKNOWN }; int sz; const string_t *tkstr; // append addtional return value comment for // return $A and return NUMBER if (ret_obj->type == A) { real_ret->type = decode_return_k (real_ret, A_reg); tkstr = token_pairs + real_ret->type; sz = snprintf (formatted_val, 0x28, "# A = %#x, %.*s", A_reg, tkstr->len, tkstr->start); } else if (ret_obj->type == NUMBER) { real_ret->type = decode_return_k (real_ret, ret_obj->data); tkstr = token_pairs + real_ret->type; sz = snprintf (formatted_val, 0x28, "# %.*s", tkstr->len, tkstr->start); } else return (string_t){ 0 }; register token_type tk = real_ret->type; if (tk == TRACE || tk == TRAP || tk == ERRNO) sz += snprintf (formatted_val + sz, 0x28 - sz, "(%u)", real_ret->data); assert (sz); return (string_t){ .start = formatted_val, .len = sz }; } static uint32_t code_nr_to_text_nr (vector_t *text_v, vector_t *code_ptr_v, statement_t *cur, label_t *jmp) { if (jmp->key.len == 0) // if ... goto xxx; false case -> jmp.code_nr is NULL return cur->text_nr + 1; statement_t **ptr = get_vector (code_ptr_v, cur->code_nr + jmp->code_nr + 1); uint32_t text_nr = (*ptr)->text_nr; statement_t *statement; string_t *label_decl; uint32_t cur_line_nr = cur->text_nr; // compiler hint while (text_nr > cur_line_nr) { statement = get_vector (text_v, text_nr); label_decl = &statement->label_decl; if ((label_decl->start) && (label_decl->len == jmp->key.len) && (!strncmp (label_decl->start, jmp->key.start, jmp->key.len))) break; text_nr--; } assert (text_nr != cur_line_nr); // should find a tag to jump to return text_nr; } static void print_label_decl (statement_t *statement) { string_t *label_decl = &statement->label_decl; if (label_decl->start) { fwrite (label_decl->start, 1, label_decl->len, stdout); fputc (':', stdout); fputc (' ', stdout); } } static void emulate_printer (statement_t *statement, bool is_skipped, bool quiet) { if (quiet) return; bool global_color = color_enable; if (is_skipped && global_color) { fwrite (LIGHTCLR, 1, LITERAL_STRLEN (LIGHTCLR), stdout); push_color (false); } print_label_decl (statement); print_statement (stdout, statement); if (is_skipped && global_color) { pop_color (); fwrite (CLR, 1, LITERAL_STRLEN (CLR), stdout); } } static statement_t * emulator (vector_t *text_v, vector_t *code_ptr_v, bool quiet) { uint32_t read_idx = 1; uint32_t exec_idx = 1; label_t *jmp; statement_t *statement = NULL; for (; read_idx < text_v->count; read_idx++) { statement = get_vector (text_v, read_idx); if (read_idx < exec_idx) { emulate_printer (statement, true, quiet); continue; } exec_idx++; switch (statement->type) { case ASSIGN_LINE: assign_line (&statement->assign_line); break; case JUMP_LINE: jmp = jump_line (&statement->jump_line); exec_idx = code_nr_to_text_nr (text_v, code_ptr_v, statement, jmp); break; case RETURN_LINE: // make compiler happy ; obj_t real_ret; string_t ret_str = return_line (&statement->return_line, &real_ret); if (ret_str.start) { if (quiet) statement->return_line.ret_obj = real_ret; else { statement->line_start = (char *)ret_str.start; statement->comment = 0; statement->line_len = ret_str.len; } } goto out; case EMPTY_LINE: break; case EOF_LINE: case ERROR_LINE: assert (!"Emulating EOF/ERROR line??"); } emulate_printer (statement, false, quiet); } out: assert (statement); assert (statement->type == RETURN_LINE); emulate_printer (statement, false, quiet); return statement; } static void init_attr (emu_arg_t *emu_arg) { if (emu_arg->sys_name == NULL) error ("%s", M_INPUT_SYS_NR); syscall_nr = seccomp_syscall_resolve_name_arch (emu_arg->scmp_arch, emu_arg->sys_name); if ((int32_t)syscall_nr == __NR_SCMP_ERROR) { char *end; syscall_nr = strtoull (emu_arg->sys_name, &end, 0); if (*end != '\0') error ("%s", M_INVALID_SYSNR); } scmp_arch = emu_arg->scmp_arch; low_pc = emu_arg->ip & UINT32_MAX; high_pc = emu_arg->ip >> 32; for (uint32_t i = 0; i < 6; i++) { low_args[i] = emu_arg->args[i] & UINT32_MAX; high_args[i] = emu_arg->args[i] >> 32; } } void emulate_v (vector_t *text_v, vector_t *code_ptr_v, emu_arg_t *emu_arg, FILE *output_fp) { init_attr (emu_arg); statement_t *ret = emulator (text_v, code_ptr_v, emu_arg->quiet); uint32_t line_left = text_v->count - 1 - ret->text_nr; if (!emu_arg->quiet && line_left) print_as_comment (output_fp, "... %d line(s) skipped", line_left); if (!emu_arg->quiet) return; extern_obj_printer (output_fp, &ret->return_line.ret_obj); fputc ('\n', output_fp); } void emulate (emu_arg_t *emu_arg) { size_t lines = init_source (emu_arg->text_file) + 1; init_scanner (next_line ()); init_parser (emu_arg->scmp_arch); init_table (); vector_t text_v; vector_t code_ptr_v; init_vector (&text_v, sizeof (statement_t), lines); init_vector (&code_ptr_v, sizeof (statement_t *), MIN (lines, 1025)); parser (&text_v, &code_ptr_v); if (resolver (&code_ptr_v)) error ("%s", M_EMU_TERMINATED); // if ERROR_LINE exists, then exits emulate_v (&text_v, &code_ptr_v, emu_arg, stdout); free_table (); free_source (); free_vector (&text_v); free_vector (&code_ptr_v); } ceccomp-4.0/src/lexical/000077500000000000000000000000001514205130000151655ustar00rootroot00000000000000ceccomp-4.0/src/lexical/parser.c000066400000000000000000000245541514205130000166370ustar00rootroot00000000000000#include "lexical/parser.h" #include "lexical/scanner.h" #include "lexical/token.h" #include "main.h" #include "utils/arch_trans.h" #include "utils/error.h" #include "utils/hash.h" #include "utils/logger.h" #include "utils/vector.h" #include #include #include #include #include typedef struct { uint16_t text_nr; uint16_t code_nr; token_t previous; token_t current; token_t next; } parse_t; static parse_t parse = { .text_nr = 0, .code_nr = 0 }; static statement_t *local; static uint32_t local_arch; static jmp_buf g_env; static void error_at (token_t token, const char *err_msg); static void advance (void) { parse.previous = parse.current; parse.current = parse.next; scan_token (&parse.next); } static bool peek (token_type expected) { return expected == parse.next.type; } static bool peek_from_to (token_type from, token_type to) { return parse.next.type >= from && parse.next.type <= to; } static bool match (token_type expected) { if (!peek (expected)) return false; advance (); return true; } static bool match_number (void) { if (LIKELY (peek (NUMBER))) return advance (), true; if (peek (OVERFLOW_NUMBER)) error_at (parse.next, M_NUMBER_OVERFLOW); return false; } static bool match_from_to (token_type expected_start, token_type expected_end) { if (!peek_from_to (expected_start, expected_end)) return false; advance (); return true; } static void error_at (token_t token, const char *err_msg) { // sync to the nextline while (!(match (EOL) || peek (TOKEN_EOF))) advance (); local->type = ERROR_LINE; local->line_len = parse.current.token_start - local->line_start; local->error_line.error_start = token.token_start; local->error_line.error_msg = err_msg; longjmp (g_env, 1); } // paren_num can be ignored, default as 0 static uint32_t paren_num (void) { if (!match (LEFT_PAREN)) return 0; if (!match_number ()) error_at (parse.next, M_EXPECT_NUMBER); if (!match (RIGHT_PAREN)) error_at (parse.next, M_EXPECT_PAREN); return parse.previous.data; } // but bracket_num can not be ignored static uint32_t bracket_num (void) { if (!match (LEFT_BRACKET)) error_at (parse.next, M_EXPECT_BRACKET); if (!match_number ()) error_at (parse.next, M_EXPECT_NUMBER); if (!match (RIGHT_BRACKET)) error_at (parse.next, M_EXPECT_BRACKET); return parse.previous.data; } static void empty_line (void) { local->type = EMPTY_LINE; parse.code_nr--; // empty_line doesn't count return; } static void eof_line (void) { local->type = EOF_LINE; return; } static void return_line (void) { local->type = RETURN_LINE; obj_t *obj = &local->return_line.ret_obj; obj->literal.start = parse.next.token_start; if (match_from_to (TRACE, ERRNO)) { obj->type = parse.current.type; obj->data = paren_num (); } else if (match_number ()) { obj->type = NUMBER; obj->data = parse.current.data; } else if (match_from_to (KILL_PROC, LOG) || match (A)) obj->type = parse.current.type; else error_at (parse.next, M_EXPECT_RETURN_VAL); obj->literal.len = parse.current.token_start + parse.current.token_len - obj->literal.start; // some obj consume more than one token like TRACE(0xf) } static void label (label_t *label) { if (!match (IDENTIFIER)) error_at (parse.next, M_EXPECT_LABEL); label->key.start = parse.current.token_start; label->key.len = parse.current.token_len; } static uint32_t resolve_name_arch (uint32_t arch_token, token_t *sys_token) { char *sysname = sys_token->token_start; char *strend = sys_token->token_start + sys_token->token_len; char save = *strend; *strend = '\0'; uint32_t sys_nr = seccomp_syscall_resolve_name_arch (arch_token, sysname); *strend = save; return sys_nr; } static void compare_obj (obj_t *obj) { obj->literal.start = parse.next.token_start; obj->literal.len = parse.next.token_len; if (match (X)) { obj->type = parse.current.type; return; } obj->type = NUMBER; if (match_number ()) { obj->data = parse.current.data; return; } if (match (IDENTIFIER)) { obj->data = resolve_name_arch (local_arch, &parse.current); if (obj->data == (uint32_t)-1) error_at (parse.current, M_EXPECT_SYSCALL); return; } // read if (!match_from_to (ARCH_X86, ARCH_RISCV64)) error_at (parse.next, M_UNEXPECT_TOKEN); if (!match (DOT)) { obj->data = internal_arch_to_scmp_arch (parse.current.type); if (UNLIKELY (obj->data == (uint32_t)-1)) // libseccomp does not support input arch?? error_at (parse.current, M_EXPECT_ARCH); return; } // i386 if (!peek (IDENTIFIER)) error_at (parse.next, M_EXPECT_SYSCALL); uint32_t scmp_arch = internal_arch_to_scmp_arch (parse.previous.type); if (UNLIKELY (scmp_arch == (uint32_t)-1)) // libseccomp does not support input arch?? error_at (parse.previous, M_EXPECT_ARCH); obj->data = resolve_name_arch (scmp_arch, &parse.next); if (obj->data == (uint32_t)-1) error_at (parse.next, M_EXPECT_SYSCALL); obj->literal.len += parse.next.token_len + 1; // +1 is for dot advance (); // i386.read } static void condition (jump_line_t *jump_line) { jump_line->if_condition = true; if (match (BANG)) jump_line->if_bang = true; if (!match (LEFT_PAREN)) error_at (parse.next, M_EXPECT_PAREN); if (!match (A)) error_at (parse.next, M_EXPECT_A); if (!match_from_to (EQUAL_EQUAL, AND)) error_at (parse.next, M_EXPECT_COMPARTOR); jump_line->comparator = parse.current.type; compare_obj (&jump_line->cmpobj); if (!match (RIGHT_PAREN)) error_at (parse.next, M_EXPECT_PAREN); } static void ja_line (void) { local->type = JUMP_LINE; jump_line_t *jump_line = &local->jump_line; jump_line->if_condition = false; label (&jump_line->jt); } static void jump_line (void) { local->type = JUMP_LINE; jump_line_t *jump_line = &local->jump_line; condition (jump_line); if (!match (GOTO)) error_at (parse.next, M_EXPECT_GOTO); label (&jump_line->jt); jump_line->jf.key.start = NULL; // jf default as zero if (!match (COMMA)) return; // probably this jump_line has no jf if (!match (ELSE)) error_at (parse.next, M_EXPECT_ELSE); if (!match (GOTO)) error_at (parse.next, M_EXPECT_GOTO); label (&jump_line->jf); } static void left (obj_t *obj) { obj->type = parse.current.type; // expression checked for us, it must be A X MEM here obj->literal.start = parse.current.token_start; if (parse.current.type == MEM) obj->data = bracket_num (); obj->literal.len = parse.current.token_start + parse.current.token_len - obj->literal.start; // some obj consume more than one token like mem[0xf] } static void right (obj_t *obj) { obj->literal.start = parse.next.token_start; if (match (A) || match (X) || match_from_to (ATTR_LEN, ATTR_HIGHPC)) obj->type = parse.current.type; else if (match (MEM) || match (ATTR_LOWARG) || match (ATTR_HIGHARG)) { obj->type = parse.current.type; obj->data = bracket_num (); } else if (match_number ()) { obj->type = parse.current.type; obj->data = parse.current.data; } else error_at (parse.next, M_EXPECT_RIGHT_VAR); obj->literal.len = parse.current.token_start + parse.current.token_len - obj->literal.start; // some obj consume more than one token like mem[0xf] } static void assign_line (void) { local->type = ASSIGN_LINE; assign_line_t *assign_line = &local->assign_line; left (&assign_line->left_var); if (match_from_to (ADD_TO, XOR_TO)) assign_line->operator = parse.current.type; else if (match (EQUAL)) { // if we match NEGATIVE, use NEGATIVE to overwrite operator // else use EQUAL as operator match (NEGATIVE); assign_line->operator = parse.current.type; } else error_at (parse.next, M_EXPECT_OPERATOR); right (&assign_line->right_var); } static void expression (void) { if (peek (EOL) || peek (COMMENT)) empty_line (); else if (peek (TOKEN_EOF)) eof_line (); else if (UNLIKELY (parse.code_nr > 1024)) error ("%s", M_STATEMENT_OVERFLOW); else if (match (RETURN)) return_line (); else if (match (IF)) jump_line (); else if (match (GOTO)) ja_line (); else if (match_from_to (A, MEM)) assign_line (); else error_at (parse.next, M_UNEXPECT_TOKEN); if (match (COMMENT)) local->comment = parse.current.token_start - local->line_start; else local->comment = -1; if (match (EOL)) local->line_len = parse.current.token_start - local->line_start; else if (peek (TOKEN_EOF)) local->line_len = parse.next.token_start - local->line_start; else error_at (parse.next, M_UNEXPECT_TOKEN); } static void label_decl (string_t *label_decl) { if (!match (LABEL_DECL)) return; label_decl->start = parse.current.token_start; label_decl->len = parse.current.token_len - 1; // ignore the ':' character if (insert_key (label_decl, parse.code_nr) == 1) error_at (parse.current, M_DUPLICATED_LABEL); // ignore our disasm useless output uint32_t count = 0; while (match_number () && count < 4) count++; } static void parse_line (statement_t *statement) { local = statement; memset (local, 0, sizeof (statement_t)); parse.text_nr++; parse.code_nr++; // if statement turns out to be empty_line, code_line--; local->code_nr = parse.code_nr; local->text_nr = parse.text_nr; local->label_decl.start = NULL; // label_decl.string default as NULL local->line_start = parse.next.token_start; if (setjmp (g_env) == 1) return; label_decl (&statement->label_decl); expression (); return; } void init_parser (uint32_t scmp_arch) { advance (); local_arch = scmp_arch; } void parser (vector_t *text_v, vector_t *code_ptr_v) { statement_t statement = { 0 }; statement_t *ptr = &statement; push_vector (text_v, &statement); push_vector (code_ptr_v, &ptr); while (true) { parse_line (&statement); if (statement.type == EOF_LINE) break; push_vector (text_v, &statement); } // we have to do it after text vector finish // because text vector might reallocate for (uint32_t i = 1; i < text_v->count; i++) { ptr = get_vector (text_v, i); if (ptr->type == EMPTY_LINE) continue; push_vector (code_ptr_v, &ptr); } } ceccomp-4.0/src/lexical/scanner.c000066400000000000000000000167101514205130000167670ustar00rootroot00000000000000#include "lexical/scanner.h" #include "config.h" #include "lexical/token.h" #include "main.h" #include "utils/arch_trans.h" #include "utils/read_source.h" #include #include #include #include #include #include #include #include #include static scanner_t scanner; static uint8_t unknown_count = 0; #define INIT_TOKEN(type) \ do \ { \ init_token (token, &scanner, type); \ return; \ } \ while (0) #define INIT_TOKEN_DATA(type, data) \ do \ { \ init_token_data (token, &scanner, type, data); \ return; \ } \ while (0) #define INIT_TOKEN_ADV1(type) \ do \ { \ advance (1); \ INIT_TOKEN (type); \ } \ while (0) static inline bool isidentifier (char c) { return isalnum_l (c, lc_c) || c == '_'; } static char peek (uint32_t offset) { return scanner.current_char[offset]; } static void advance (uint16_t advance_len) { scanner.current_char += advance_len; } static bool match_offset (char expected, uint32_t offset) { if (peek (offset) != expected) return false; advance (offset + 1); return true; } static bool match (char expected) { return match_offset (expected, 0); } static bool match_token (token_type tk) { register string_t token = token_pairs[tk]; if (strncmp (token.start, scanner.current_char, token.len)) return false; advance (token.len); return true; } // from and to all included static token_type match_token_range (token_type from, token_type to) { for (uint32_t i = from; i <= to; i++) if (match_token (i)) return i; return UNKNOWN; } static void reset_to_nextline (token_t *token) { init_token (token, &scanner, EOL); // if nextline == NULL, it means we meet_eof char *nextline = next_line (); scanner.token_start = nextline; scanner.current_char = scanner.token_start; scanner.line_nr++; } void init_scanner (char *start) { scanner.token_start = start; scanner.current_char = start; scanner.line_nr = 1; } void scan_token (token_t *token) { // if scanner.token_start == NULL, we meet_eof if (scanner.token_start == NULL) INIT_TOKEN (TOKEN_EOF); // skip spaces while (isspace_l (peek (0), lc_c) && peek (0) != '\n') advance (1); // sync scanner.token_start = scanner.current_char; // COMMENT if (match ('#')) { while (peek (0) != '\n') advance (1); INIT_TOKEN (COMMENT); } // EOL if (match ('\n')) { reset_to_nextline (token); return; } char cur_char = peek (0); if (islower_l (cur_char, lc_c)) { token_type tk = match_token_range (RETURN, ELSE); if (tk != UNKNOWN) INIT_TOKEN (tk); tk = str_to_internal_arch (scanner.current_char); if (tk != UNKNOWN) { advance (token_pairs[tk].len); INIT_TOKEN (tk); } } else if (isupper_l (cur_char, lc_c)) { token_type tk = match_token_range (KILL_PROC, ERRNO); if (tk != UNKNOWN) INIT_TOKEN (tk); } else if (ispunct_l (cur_char, lc_c)) { switch (cur_char) { case '$': if (match_offset ('A', 1)) INIT_TOKEN (A); if (match_offset ('X', 1)) INIT_TOKEN (X); token_type tk = match_token_range (MEM, ATTR_HIGHARG); if (tk != UNKNOWN) INIT_TOKEN (tk); break; case ',': INIT_TOKEN_ADV1 (COMMA); case '.': INIT_TOKEN_ADV1 (DOT); case '[': INIT_TOKEN_ADV1 (LEFT_BRACKET); case ']': INIT_TOKEN_ADV1 (RIGHT_BRACKET); case '(': INIT_TOKEN_ADV1 (LEFT_PAREN); case ')': INIT_TOKEN_ADV1 (RIGHT_PAREN); case '+': if (match_offset ('=', 1)) INIT_TOKEN (ADD_TO); break; case '-': if (match_offset ('=', 1)) INIT_TOKEN (SUB_TO); INIT_TOKEN_ADV1 (NEGATIVE); case '*': if (match_offset ('=', 1)) INIT_TOKEN (MULTI_TO); break; case '/': if (match_offset ('=', 1)) INIT_TOKEN (DIVIDE_TO); break; case '<': if (match_offset ('=', 1)) INIT_TOKEN (LESS_EQUAL); if (peek (1) == '<' && match_offset ('=', 2)) INIT_TOKEN (LSH_TO); INIT_TOKEN_ADV1 (LESS_THAN); case '>': if (match_offset ('=', 1)) INIT_TOKEN (GREATER_EQUAL); if (peek (1) == '>' && match_offset ('=', 2)) INIT_TOKEN (RSH_TO); INIT_TOKEN_ADV1 (GREATER_THAN); case '&': if (match_offset ('=', 1)) INIT_TOKEN (AND_TO); INIT_TOKEN_ADV1 (AND); case '|': if (match_offset ('=', 1)) INIT_TOKEN (OR_TO); break; case '^': if (match_offset ('=', 1)) INIT_TOKEN (XOR_TO); break; case '!': if (match_offset ('=', 1)) INIT_TOKEN (BANG_EQUAL); INIT_TOKEN_ADV1 (BANG); case '=': if (match_offset ('=', 1)) INIT_TOKEN (EQUAL_EQUAL); INIT_TOKEN_ADV1 (EQUAL); } goto unknown; } // LABEL_DECL : IDENTIFIER // IDENTIFIER include SYSCALL and label // We don't want hash the SYSCALL, so leave it later if (isalpha_l (peek (0), lc_c)) { do advance (1); while (isidentifier (peek (0))); INIT_TOKEN (match (':') ? LABEL_DECL : IDENTIFIER); } // NUMBER if (isdigit_l (peek (0), lc_c)) { char *end; unsigned long num = strtoul (scanner.token_start, &end, 0); if (scanner.token_start == end) goto unknown; #if __SIZEOF_POINTER__ == 8 static_assert (sizeof (unsigned long) == 8, "64-bit machine has 32-bit long?"); if ((uint32_t)num != num) INIT_TOKEN_ADV1 (OVERFLOW_NUMBER); #else #include static_assert (sizeof (unsigned long) == 4, "long is not 32-bit on 32-bit machine?"); if (num == ULONG_MAX && errno == ERANGE) INIT_TOKEN_ADV1 (OVERFLOW_NUMBER); #endif scanner.current_char = end; INIT_TOKEN_DATA (NUMBER, (uint32_t)num); } unknown: // perhaps that's a wrong file? tell parser to stop using EOF if (unknown_count > 5) INIT_TOKEN (TOKEN_EOF); unknown_count += 1; advance (1); INIT_TOKEN (UNKNOWN); } ceccomp-4.0/src/lexical/token.c000066400000000000000000000060101514205130000164460ustar00rootroot00000000000000#include "lexical/token.h" #include "lexical/scanner.h" #include "main.h" #include #include #define DEFTK(token) { token, LITERAL_STRLEN (token) } // clang-format off const string_t token_pairs[] = { [ARCH_X86] = DEFTK ("i386"), [ARCH_I686] = DEFTK ("i686"), [ARCH_X86_64] = DEFTK ("x86_64"), [ARCH_X32] = DEFTK ("x32"), [ARCH_ARM] = DEFTK ("arm"), [ARCH_AARCH64] = DEFTK ("aarch64"), [ARCH_LOONGARCH64] = DEFTK ("loongarch64"), [ARCH_M68K] = DEFTK ("m68k"), [ARCH_MIPSEL64N32] = DEFTK ("mipsel64n32"), [ARCH_MIPSEL64] = DEFTK ("mipsel64"), [ARCH_MIPSEL] = DEFTK ("mipsel"), [ARCH_MIPS64N32] = DEFTK ("mips64n32"), [ARCH_MIPS64] = DEFTK ("mips64"), [ARCH_MIPS] = DEFTK ("mips"), [ARCH_PARISC64] = DEFTK ("parisc64"), [ARCH_PARISC] = DEFTK ("parisc"), [ARCH_PPC64LE] = DEFTK ("ppc64le"), [ARCH_PPC64] = DEFTK ("ppc64"), [ARCH_PPC] = DEFTK ("ppc"), [ARCH_S390X] = DEFTK ("s390x"), [ARCH_S390] = DEFTK ("s390"), [ARCH_RISCV64] = DEFTK ("riscv64"), [KILL_PROC] = DEFTK ("KILL_PROCESS"), [KILL] = DEFTK ("KILL"), [ALLOW] = DEFTK ("ALLOW"), [NOTIFY] = DEFTK ("NOTIFY"), [LOG] = DEFTK ("LOG"), [TRACE] = DEFTK ("TRACE"), [TRAP] = DEFTK ("TRAP"), [ERRNO] = DEFTK ("ERRNO"), [RETURN] = DEFTK ("return"), [IF] = DEFTK ("if"), [GOTO] = DEFTK ("goto"), [ELSE] = DEFTK ("else"), [COMMA] = DEFTK (","), [A] = DEFTK ("$A"), [X] = DEFTK ("$X"), [MEM] = DEFTK ("$mem"), [ATTR_LEN] = DEFTK ("$scmp_data_len"), [ATTR_SYSCALL] = DEFTK ("$syscall_nr"), [ATTR_ARCH] = DEFTK ("$arch"), [ATTR_LOWPC] = DEFTK ("$low_pc"), [ATTR_HIGHPC] = DEFTK ("$high_pc"), [ATTR_LOWARG] = DEFTK ("$low_args"), [ATTR_HIGHARG] = DEFTK ("$high_args"), [DOT] = DEFTK ("."), [LEFT_BRACKET] = DEFTK ("["), [RIGHT_BRACKET] = DEFTK ("]"), [LEFT_PAREN] = DEFTK ("("), [RIGHT_PAREN] = DEFTK (")"), [ADD_TO] = DEFTK ("+="), [SUB_TO] = DEFTK ("-="), [MULTI_TO] = DEFTK ("*="), [DIVIDE_TO] = DEFTK ("/="), [LSH_TO] = DEFTK ("<<="), [RSH_TO] = DEFTK (">>="), [AND_TO] = DEFTK ("&="), [OR_TO] = DEFTK ("|="), [XOR_TO] = DEFTK ("^="), [EQUAL_EQUAL] = DEFTK ("=="), [BANG_EQUAL] = DEFTK ("!="), [GREATER_EQUAL] = DEFTK (">="), [GREATER_THAN] = DEFTK (">"), [LESS_EQUAL] = DEFTK ("<="), [LESS_THAN] = DEFTK ("<"), [AND] = DEFTK ("&"), [EQUAL] = DEFTK ("="), [NEGATIVE] = DEFTK ("-"), [BANG] = DEFTK ("!"), [NUMBER] = DEFTK ("number"), [IDENTIFIER] = DEFTK ("identifier"), [UNKNOWN] = DEFTK ("unknown"), [COMMENT] = DEFTK ("#"), [EOL] = DEFTK ("line_end"), [TOKEN_EOF] = DEFTK ("eof"), [LABEL_DECL] = DEFTK ("label_decl"), [OVERFLOW_NUMBER] = DEFTK("ERANGE"), // label_decl ::= IDENTIFIER + DEFTK(":") }; // clang-format on void init_token (token_t *token, scanner_t *scanner, token_type type) { token->type = type; token->token_start = scanner->token_start; token->token_len = scanner->current_char - scanner->token_start; token->line_nr = scanner->line_nr; } void init_token_data (token_t *token, scanner_t *scanner, token_type type, uint32_t data) { init_token (token, scanner, type); token->data = data; } ceccomp-4.0/src/probe.c000066400000000000000000000042221514205130000150170ustar00rootroot00000000000000#include "probe.h" #include "emu.h" #include "lexical/parser.h" #include "lexical/scanner.h" #include "main.h" #include "resolver/resolver.h" #include "trace.h" #include "utils/error.h" #include "utils/logger.h" #include "utils/parse_args.h" #include "utils/read_source.h" #include "utils/vector.h" #include #include #include #include #include #include #include #include #include static const char *to_test_list[] = { "open", "openat", "read", "write", "execve", "execveat", "mmap", "mprotect", "sendfile", "ptrace", "fork" }; static uint32_t fetch_text_from_trace (FILE **text, char *argv[], bool quiet) { *text = tmpfile (); if (*text == NULL) error (M_REQUEST_TMPFILE_FAILED, strerror (errno)); uint32_t scmp_arch = program_trace (argv, *text, quiet, true); fflush (*text); fseek (*text, 0, SEEK_SET); return scmp_arch; } static void init_emu_arg (emu_arg_t *emu_arg, FILE *text, uint32_t scmp_arch) { for (uint32_t i = 0; i < 6; i++) emu_arg->args[i] = 0; emu_arg->quiet = true; emu_arg->text_file = text; emu_arg->scmp_arch = scmp_arch; emu_arg->ip = 0; } void probe (char *argv[], FILE *output_fp, bool quiet) { FILE *text; emu_arg_t emu_arg; uint32_t scmp_arch; scmp_arch = fetch_text_from_trace (&text, argv, quiet); init_emu_arg (&emu_arg, text, scmp_arch); vector_t text_v; vector_t code_ptr_v; size_t lines = init_source (text) + 1; init_scanner (next_line ()); init_parser (scmp_arch); init_table (); init_vector (&text_v, sizeof (statement_t), lines); init_vector (&code_ptr_v, sizeof (statement_t *), MIN (lines, 1025)); parser (&text_v, &code_ptr_v); if (resolver (&code_ptr_v)) error ("%s", M_PROBE_TERMINATED); // if ERROR_LINE exists, then exits for (size_t i = 0; i < ARRAY_SIZE (to_test_list); i++) { fprintf (output_fp, "%-10s-> ", to_test_list[i]); emu_arg.sys_name = to_test_list[i]; emulate_v (&text_v, &code_ptr_v, &emu_arg, output_fp); } free_table (); free_source (); free_vector (&text_v); free_vector (&code_ptr_v); fclose (text); } ceccomp-4.0/src/resolver/000077500000000000000000000000001514205130000154055ustar00rootroot00000000000000ceccomp-4.0/src/resolver/render.c000066400000000000000000000121321514205130000170270ustar00rootroot00000000000000#include "resolver/render.h" #include "lexical/parser.h" #include "lexical/token.h" #include "main.h" #include "utils/arch_trans.h" #include "utils/str_pile.h" #include "utils/vector.h" #include #include #include #include #include #include #include #include static statement_t *local; static uint32_t default_arch; typedef enum { NONE = 0, // 0b00 ARCH = 1, // 0b01 SYSNR = 2, // 0b10 MIXED = 3, // 0b11 } stat_t; typedef struct { uint8_t A_stat; uint8_t X_stat; uint8_t mem_stat[BPF_MEMWORDS]; uint32_t arch; } stat_ctx_t; static stat_ctx_t *list; #define FORCE true static void set_stat (uint8_t *dest, uint8_t src, bool force) { if (force || *dest == NONE) *dest = src; else if (*dest != src) *dest = MIXED; // if *dest != src, we give up tracing. } // same as set_stat static void set_arch (uint32_t dest_idx, uint32_t src) { if (list[dest_idx].arch == NONE) list[dest_idx].arch = src; else if (list[dest_idx].arch != src) list[dest_idx].arch = MIXED; } static void set_ctx (uint32_t dest_idx, uint32_t src_idx, bool force) { set_stat (&list[dest_idx].A_stat, list[src_idx].A_stat, force); set_stat (&list[dest_idx].X_stat, list[src_idx].X_stat, force); for (uint32_t i = 0; i < BPF_MEMWORDS; i++) set_stat (&list[dest_idx].mem_stat[i], list[src_idx].mem_stat[i], force); } static void assign_line (assign_line_t *assign_line, stat_ctx_t *ctx) { obj_t *left = &assign_line->left_var; token_type op = assign_line->operator; obj_t *right = &assign_line->right_var; uint8_t *left_stat; uint8_t right_stat; if (left->type == A) left_stat = &ctx->A_stat; else if (left->type == X) left_stat = &ctx->X_stat; else if (left->type == MEM) left_stat = &ctx->mem_stat[right->data]; else assert (!"Unknown left value type"); if (op != EQUAL) right_stat = MIXED; else if (right->type == ATTR_SYSCALL) right_stat = SYSNR; else if (right->type == ATTR_ARCH) right_stat = ARCH; else if (right->type == A) right_stat = ctx->A_stat; else if (right->type == X) right_stat = ctx->X_stat; else if (right->type == MEM) right_stat = ctx->mem_stat[right->data]; else right_stat = MIXED; set_stat (left_stat, right_stat, FORCE); } static void try_resolve_arch (obj_t *cmpobj) { const string_t *arch_str = scmp_arch_to_str (cmpobj->data); if (arch_str == NULL) return; cmpobj->type = IDENTIFIER; cmpobj->literal = *arch_str; } static void try_resolve_sysnr (obj_t *cmpobj) { uint32_t cur_arch = list[local->code_nr].arch; if (cur_arch == NONE) return; char *sys_name = seccomp_syscall_resolve_num_arch (cur_arch, cmpobj->data); if (sys_name == NULL) return; cmpobj->type = IDENTIFIER; const string_t *arch_str = NULL; if (cur_arch != default_arch) arch_str = scmp_arch_to_str (cur_arch); cmpobj->literal = persist_object (sys_name, arch_str); free (sys_name); } static void jump_line (jump_line_t *jump_line) { if (!jump_line->if_condition) { // ja line uint32_t pc = local->code_nr; uint32_t jt = pc + jump_line->jt.code_nr + 1; set_ctx (jt, pc, !FORCE); set_arch (jt, list[pc].arch); return; } uint32_t pc = local->code_nr; uint8_t jt = pc + jump_line->jt.code_nr + 1; uint8_t jf = pc + jump_line->jf.code_nr + 1; token_type cmp_op = jump_line->comparator; uint32_t cmp_data = jump_line->cmpobj.data; set_ctx (jt, pc, !FORCE); set_ctx (jf, pc, !FORCE); // It's hard and unnessary to handle other comparators // So just set_arch when comparator is EQUAL_EQUAL if (list[pc].A_stat != ARCH) { set_arch (jt, list[pc].arch); set_arch (jf, list[pc].arch); } else if (cmp_op == EQUAL_EQUAL || cmp_op == BANG_EQUAL) { set_arch ((cmp_op == EQUAL_EQUAL) ? jt : jf, cmp_data); set_arch ((cmp_op == EQUAL_EQUAL) ? jf : jt, MIXED); } obj_t *cmpobj = &jump_line->cmpobj; if (list[pc].A_stat == ARCH) try_resolve_arch (cmpobj); else if (list[pc].A_stat == SYSNR) try_resolve_sysnr (cmpobj); } static void render_statement (statement_t *statement) { local = statement; switch (local->type) { case ASSIGN_LINE: assign_line (&local->assign_line, &list[local->code_nr]); set_ctx (local->code_nr + 1, local->code_nr, !FORCE); set_arch (local->code_nr + 1, list[local->code_nr].arch); break; case JUMP_LINE: jump_line (&local->jump_line); break; case RETURN_LINE: break; case EMPTY_LINE: case EOF_LINE: case ERROR_LINE: assert (!"type shouldn't be EMPTY, EOF or ERROR"); default: assert (!"Unknown type"); } } void render (vector_t *v, uint32_t scmp_arch) { default_arch = scmp_arch; uint32_t list_len = sizeof (stat_ctx_t) * (v->count + 1); // statement code_nr starts from 1 list = reallocate (NULL, list_len); memset (list, NONE, list_len); // set default_arch in list[1] list[1].arch = scmp_arch; for (uint32_t i = 1; i < v->count; i++) render_statement (get_vector (v, i)); reallocate (list, 0); } ceccomp-4.0/src/resolver/resolver.c000066400000000000000000000200611514205130000174110ustar00rootroot00000000000000#include "resolver/resolver.h" #include "lexical/parser.h" #include "lexical/token.h" #include "utils/error.h" #include "utils/hash.h" #include "utils/logger.h" #include "utils/vector.h" #include #include #include #include #include #include static bool has_error; static statement_t *local; static uint16_t *masks, mem_valid = 0; static uint16_t bpf_len = 0; #define REPORT_ERROR(error_msg) \ do \ { \ report_error (error_msg); \ return; \ } \ while (0) #define SPRINTF_CAT(...) print += sprintf (print, __VA_ARGS__) static void report_error (const char *error_msg) { has_error = true; char buf[0x400]; char *print = buf; SPRINTF_CAT ("%d:-: %s\n", local->text_nr, error_msg); // During the resolution phase, errors do not have a clear starting point // errors in this stage indicate a semantic error in an entire line of code. SPRINTF_CAT ("%.*s\n", local->line_len, local->line_start); // line_len shouldn't include a '\n' memset (print, '~', local->line_len); print[local->line_len] = '\0'; // warn will print a '\n' warn ("%s", buf); } static bool match_from_to (token_type type, token_type from, token_type to) { return type >= from && type <= to; } static void error_line (void) { has_error = true; char buf[0x400]; char *print = buf; error_line_t *error_line = &local->error_line; uint16_t err_len = error_line->error_start - local->line_start; SPRINTF_CAT ("%d:%d: %s\n", local->text_nr, err_len + 1, error_line->error_msg); SPRINTF_CAT ("%.*s\n", local->line_len, local->line_start); // line_len shouldn't include a '\n' memset (print, ' ', err_len); print[err_len] = '^'; print[err_len + 1] = '\0'; // warn will print a '\n' warn ("%s", buf); } #undef SPRINTF_CAT #define IS_ARG_OUT_RANGE(obj) is_out_range (obj, 5) #define IS_MEM_OUT_RANGE(obj) is_out_range (obj, 15) static bool is_out_range (obj_t *obj, uint32_t max_idx) { return obj->data > max_idx; } static void assign_A (assign_line_t *assign_line) { token_type operator = assign_line->operator; obj_t *right = &assign_line->right_var; if (operator == NEGATIVE) { if (right->type != A) REPORT_ERROR (M_RIGHT_SHOULD_BE_A); return; } else if (match_from_to (operator, ADD_TO, XOR_TO)) { if (right->type != X && right->type != NUMBER) REPORT_ERROR (M_RIGHT_SHOULD_BE_X_OR_NUM); if (operator == DIVIDE_TO && right->type == NUMBER && right->data == 0) REPORT_ERROR (M_ALU_DIV_BY_ZERO); // clang-format off if (match_from_to (operator, LSH_TO, RSH_TO) && right->type == NUMBER && right->data >= 32) // clang-format on REPORT_ERROR (M_ALU_SH_OUT_OF_RANGE); return; } assert (operator == EQUAL); if (right->type == A) REPORT_ERROR (M_RIGHT_CAN_NOT_BE_A); else if ((right->type == ATTR_LOWARG || right->type == ATTR_HIGHARG) && IS_ARG_OUT_RANGE (right)) REPORT_ERROR (M_ARGS_IDX_OUT_OF_RANGE); else if (right->type == MEM) { if (IS_MEM_OUT_RANGE (right)) REPORT_ERROR (M_MEM_IDX_OUT_OF_RANGE); if (!(mem_valid & (1 << right->data))) REPORT_ERROR (M_UNINITIALIZED_MEM); } } static void assign_X (assign_line_t *assign_line) { token_type *operator = &assign_line->operator; obj_t *right = &assign_line->right_var; if (*operator != EQUAL) REPORT_ERROR (M_OPERATOR_SHOULD_BE_EQUAL); if (match_from_to (right->type, ATTR_SYSCALL, ATTR_HIGHARG)) REPORT_ERROR (M_LEFT_SHOULD_BE_A); else if (right->type == X) REPORT_ERROR (M_RIGHT_CAN_NOT_BE_X); else if ((right->type == ATTR_LOWARG || right->type == ATTR_HIGHARG) && IS_ARG_OUT_RANGE (right)) REPORT_ERROR (M_ARGS_IDX_OUT_OF_RANGE); else if (right->type == MEM) { if (IS_MEM_OUT_RANGE (right)) REPORT_ERROR (M_MEM_IDX_OUT_OF_RANGE); if (!(mem_valid & (1 << right->data))) REPORT_ERROR (M_UNINITIALIZED_MEM); } } static void assign_MEM (assign_line_t *assign_line) { obj_t *left = &assign_line->left_var; token_type operator = assign_line->operator; obj_t *right = &assign_line->right_var; if (operator != EQUAL) REPORT_ERROR (M_OPERATOR_SHOULD_BE_EQUAL); if (IS_MEM_OUT_RANGE (left)) REPORT_ERROR (M_MEM_IDX_OUT_OF_RANGE); if (right->type != A && right->type != X) REPORT_ERROR (M_RIGHT_SHOULD_BE_A_OR_X); mem_valid |= (1 << left->data); } static void assign_line (void) { assign_line_t *assign_line = &local->assign_line; if (assign_line->left_var.type == A) assign_A (assign_line); else if (assign_line->left_var.type == X) assign_X (assign_line); else if (assign_line->left_var.type == MEM) assign_MEM (assign_line); } static void set_jt_jf (label_t *label, uint32_t code_nr) { label->code_nr = code_nr - local->code_nr - 1; } static void jump_line (void) { jump_line_t *jump_line = &local->jump_line; if (!jump_line->if_condition) { uint32_t jt = find_key (&jump_line->jt.key); if (jt == (uint16_t)-1) REPORT_ERROR (M_CANNOT_FIND_LABEL); if (jt > bpf_len) REPORT_ERROR (M_JA_OUT_OF_FILTERS); set_jt_jf (&jump_line->jt, jt); masks[jt] &= mem_valid; mem_valid = ~0; return; } uint32_t jt = find_key (&jump_line->jt.key); if (jt == (uint16_t)-1) REPORT_ERROR (M_CANNOT_FIND_LABEL); set_jt_jf (&jump_line->jt, jt); if ((int16_t)jump_line->jt.code_nr < 0) REPORT_ERROR (M_JT_MUST_BE_POSITIVE); if (jump_line->jt.code_nr > UINT8_MAX) REPORT_ERROR (M_JT_TOO_FAR); if (jt > bpf_len) REPORT_ERROR (M_JT_INVALID_TAG); uint32_t jf; if (jump_line->jf.key.start == NULL) jf = local->code_nr + 1; else { jf = find_key (&jump_line->jf.key); if (jf == (uint16_t)-1) REPORT_ERROR (M_CANNOT_FIND_LABEL); } set_jt_jf (&jump_line->jf, jf); jf = local->code_nr + jump_line->jf.code_nr + 1; if ((int16_t)jump_line->jf.code_nr < 0) REPORT_ERROR (M_JF_MUST_BE_POSITIVE); if (jump_line->jf.code_nr > UINT8_MAX) REPORT_ERROR (M_JF_TOO_FAR); if (jf > bpf_len) REPORT_ERROR (M_JF_INVALID_TAG); masks[jt] &= mem_valid; masks[jf] &= mem_valid; mem_valid = ~0; } static void return_line (void) { return_line_t *return_line = &local->return_line; token_type ret_type = return_line->ret_obj.type; if (ret_type == A || ret_type == NUMBER) return; if ((ret_type == TRACE || ret_type == TRAP || ret_type == ERRNO) && return_line->ret_obj.data > 0xffff) REPORT_ERROR (M_RET_DATA_OVERFLOW); // Don't assume the ret_obj.data == zero when not used } static void resolve_statement (statement_t *statement) { local = statement; switch (local->type) { case ERROR_LINE: error_line (); return; case ASSIGN_LINE: assign_line (); return; case JUMP_LINE: jump_line (); return; case RETURN_LINE: return_line (); return; // nothing need to be done for these line case EMPTY_LINE: case EOF_LINE: assert (!"Resolving EMPTY_LINE/EOF_LINE??"); } } bool resolver (vector_t *code_ptr_v) { has_error = false; masks = reallocate (NULL, sizeof (*masks) * (code_ptr_v->count)); memset (masks, 0xff, sizeof (*masks) * (code_ptr_v->count)); mem_valid = 0; bpf_len = code_ptr_v->count - 1; if (bpf_len == 0) error ("%s", M_NO_VALID_CODE); for (uint32_t i = 1; i < code_ptr_v->count; i++) { mem_valid &= masks[i]; statement_t **ptr = get_vector (code_ptr_v, i); resolve_statement (*ptr); } statement_t **last = get_vector (code_ptr_v, code_ptr_v->count - 1); if ((*last)->type != RETURN_LINE) report_error (M_MUST_END_WITH_RET); reallocate (masks, 0x0); return has_error; } ceccomp-4.0/src/trace.c000066400000000000000000000241551514205130000150150ustar00rootroot00000000000000#include "trace.h" #include "disasm.h" #include "main.h" #include "utils/error.h" #include "utils/logger.h" #include "utils/proc_status.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOAD_FAIL 2 #define LOAD_ELSE 3 static uint64_t seccomp_nr; static uint64_t prctl_nr; static uint32_t saved_arch = -1; static uint64_t check_scmp_mode (syscall_info info, int pid, fprog *prog) { uint64_t seccomp_mode = LOAD_ELSE; uint64_t nr = info.entry.nr; uint64_t arg0 = info.entry.args[0]; uint64_t arg1 = info.entry.args[1]; if (nr == seccomp_nr && (arg0 == SECCOMP_SET_MODE_FILTER || arg0 == SECCOMP_MODE_STRICT)) seccomp_mode = arg0; else if (nr == prctl_nr && arg0 == PR_SET_SECCOMP) { if (arg1 == SECCOMP_MODE_STRICT) arg1 = SECCOMP_SET_MODE_STRICT; else if (arg1 == SECCOMP_MODE_FILTER) arg1 = SECCOMP_SET_MODE_FILTER; // prctl use different macros // transfer it to seccomp macros seccomp_mode = arg1; } else return seccomp_mode; // get seccomp_mode // prctl (PR_SET_SECCOMP, seccomp_mode, &prog); // seccomp (seccomp_mode, 0, &prog); prog->len = ptrace (PTRACE_PEEKDATA, pid, info.entry.args[2], 0); // also get filter len, maybe we need to dump it if the seccomp succeed ptrace (PTRACE_SYSCALL, pid, 0, 0); waitpid (pid, NULL, 0); ptrace (PTRACE_GET_SYSCALL_INFO, pid, sizeof (syscall_info), &info); assert (info.op == PTRACE_SYSCALL_INFO_EXIT); if (info.exit.is_error) seccomp_mode = LOAD_FAIL; // seccomp set failed, nothing happened return seccomp_mode; } static size_t peek_data_check (pid_t pid, size_t *addr) { errno = 0; size_t result = ptrace (PTRACE_PEEKDATA, pid, addr, 0); if (result == (size_t)-1 && errno != 0) error (M_PEEKDATA_FAILED_ADR, (void *)addr); return result; } static void dump_filter (syscall_info *info, int pid, fprog *prog) { size_t *filters = (size_t *)prog->filter; // args2 is the prog addrs uint64_t args2 = info->entry.args[2]; uint32_t offset = offsetof (fprog, filter); bool is_local_64 = (sizeof (void *) == 8); bool is_target_64 = info->arch & __AUDIT_ARCH_64BIT; if (is_local_64 && !is_target_64) offset /= 2; else if (!is_local_64 && is_target_64) error ("%s", M_CANNOT_WORK_FROM_32_TO_64); size_t filter_adr = peek_data_check (pid, (size_t *)((size_t)args2 + offset)); if (is_local_64 && !is_target_64) filter_adr &= 0xffffffff; // use size_t so that it can work in both 64 and 32 bits for (int i = 0; i * sizeof (size_t) < prog->len * sizeof (filter); i++) filters[i] = peek_data_check (pid, &((size_t *)filter_adr)[i]); } static void mode_filter (syscall_info *info, int pid, fprog *prog, FILE *output_fp) { prog->filter = g_filters; dump_filter (info, pid, prog); print_prog (info->arch, prog, output_fp); } __attribute__ ((noreturn)) static void child (char *argv[]) { ptrace (PTRACE_TRACEME, 0, 0, 0); raise (SIGSTOP); int err = execv (argv[0], argv); if (err) error ("%s %s: %s", M_EXECV_ERR, argv[0], strerror (errno)); exit (0); } static void handle_fork (pid_t pid, int status, bool quiet) { int event = (status >> 16) & 0xffff; if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK || event == PTRACE_EVENT_CLONE) { uint64_t new_pid; ptrace (PTRACE_GETEVENTMSG, pid, NULL, &new_pid); if (!quiet) info (M_PROCESS_FORK, pid, (pid_t)new_pid); } } static bool handle_syscall (pid_t pid, FILE *output_fp, bool quiet, bool oneshot) { syscall_info info; fprog prog; uint32_t seccomp_mode; ptrace (PTRACE_GET_SYSCALL_INFO, pid, sizeof (info), &info); if (info.op != PTRACE_SYSCALL_INFO_ENTRY) return false; if (info.arch != saved_arch) { saved_arch = info.arch; seccomp_nr = seccomp_syscall_resolve_name_arch (saved_arch, "seccomp"); prctl_nr = seccomp_syscall_resolve_name_arch (saved_arch, "prctl"); // every arch has prctl, so if prctl has no nr, seccomp has no nr, either if (prctl_nr == (uint64_t)__NR_SCMP_ERROR) error (M_TRACEE_ARCH_NOT_SUPPORTED, saved_arch); } seccomp_mode = check_scmp_mode (info, pid, &prog); if (!quiet) { if (seccomp_mode == LOAD_FAIL) warn (M_PID_BPF_LOAD_FAIL, pid); else if (seccomp_mode == SECCOMP_SET_MODE_FILTER || seccomp_mode == SECCOMP_SET_MODE_STRICT) info (M_PARSE_PID_BPF, pid); } if (seccomp_mode == SECCOMP_SET_MODE_STRICT) warn (M_FOUND_STRICT_MODE, pid); else if (seccomp_mode == SECCOMP_SET_MODE_FILTER) mode_filter (&info, pid, &prog, output_fp); if (!oneshot || seccomp_mode == LOAD_FAIL || seccomp_mode == LOAD_ELSE) return false; return true; } static uint32_t parent (pid_t child_pid, FILE *output_fp, uint32_t extra_ptrace_flags, bool quiet, bool oneshot) { int status; waitpid (child_pid, &status, 0); // child is stopped after PTRACE_TRACEME if (!quiet) // write info when we truly start tracing so that // script can continue interacting correctly info (M_START_TRACING, child_pid); // clang-format off ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | extra_ptrace_flags); // clang-format on ptrace (PTRACE_SYSCALL, child_pid, 0, 0); while (1) { pid_t pid = waitpid (-1, &status, __WALL); if (pid == -1) { if (errno == ECHILD) exit (0); continue; } if (WIFEXITED (status) || WIFSIGNALED (status)) { if (!quiet) info (M_PROCESS_EXIT, pid); continue; } if (WIFCONTINUED (status)) continue; int sig = WSTOPSIG (status); if (sig == (SIGTRAP | 0x80)) { if (handle_syscall (pid, output_fp, quiet, oneshot)) return saved_arch; ptrace (PTRACE_SYSCALL, pid, 0, 0); } else if (sig == SIGTRAP) { handle_fork (pid, status, quiet); ptrace (PTRACE_SYSCALL, pid, 0, 0); } else ptrace (PTRACE_SYSCALL, pid, 0, sig); } } static void exit_on_sig (int signo) { // flush files when recved normal signals exit (signo); } uint32_t program_trace (char *argv[], FILE *output_fp, bool quiet, bool oneshot) { signal (SIGINT, exit_on_sig); signal (SIGTERM, exit_on_sig); int pid = fork (); if (pid == 0) child (argv); else return parent (pid, output_fp, PTRACE_O_EXITKILL, quiet, oneshot); } static void einval_get_filter (pid_t pid) { seccomp_mode mode = get_proc_seccomp (pid); if ((int)mode == PROCFS_ERROR) error ("%s %s, %s", M_PROCFS_NOT_ACCESSIBLE, ACTION_GET_FILTER, M_GET_FILTER_UNSUPPORTED_OR_NO_FILTER); if (mode == STATUS_STRICT_MODE) { warn (M_FOUND_STRICT_MODE, pid); exit (0); } else if (mode == STATUS_FILTER_MODE) error ("%s", M_GET_FILTER_UNSUPPORTED); // if mode == STATUS_NONE, return to print "no filters found" } __attribute__ ((noreturn)) static void eacces_get_filter (pid_t pid) { seccomp_mode mode = get_proc_seccomp (pid); if ((int)mode == PROCFS_ERROR) error ("%s %s, %s", M_PROCFS_NOT_ACCESSIBLE, ACTION_GET_FILTER, M_CAP_SYS_ADMIN_OR_IN_SECCOMP); if (mode == STATUS_NONE) error ("%s", M_REQUIRE_CAP_SYS_ADMIN); else error ("%s", M_CECCOMP_IN_SECCOMP); } // return true means continue // else break static bool error_get_filter (pid_t pid, int err) { switch (err) { case ENOENT: return false; case EINVAL: einval_get_filter (pid); return false; case EACCES: eacces_get_filter (getpid ()); case EMEDIUMTYPE: warn ("%s", M_NOT_AN_CBPF); return true; default: error (M_UNKNOWN_GETFILTER_ERR, strerror (err)); } } __attribute__ ((noreturn)) static void eperm_seize (pid_t pid) { // seizing a thread in the same thread group may cause EPERM // but that will probably not happen kthread_mode mode = is_proc_kthread (pid); if ((int)mode == PROCFS_ERROR) error ("%s %s, %s", M_PROCFS_NOT_ACCESSIBLE, ACTION_PTRACE_SEIZE, M_CAP_SYS_PTRACE_OR_KTHREAD); if (mode == STATUS_KTHREAD) error ("%s", M_SEIZING_KERNEL_THREAD); pid_t tracer = get_tracer_pid (pid); assert (tracer != PROCFS_ERROR); if (tracer) error (M_TARGET_TRACED_BY, tracer); else error ("%s", M_REQUIRE_CAP_SYS_PTRACE); // seize needs CAP_SYS_PTRACE // get_filter needs CAP_SYS_ADMIN } static void error_seize (pid_t pid, int err) { switch (err) { case EPERM: eperm_seize (pid); case ESRCH: error ("%s", M_SEIZE_NONEXIST_PROC); default: error (M_UNKNOWN_SEIZE_ERR, strerror (err)); } } static void pid_seize (int pid, bool quiet) { signal (SIGINT, exit_on_sig); signal (SIGTERM, exit_on_sig); if (ptrace (PTRACE_ATTACH, pid, NULL, NULL) == -1) error_seize (pid, errno); parent (pid, stdout, 0, quiet, false); } void pid_trace (int pid, bool seize, bool quiet) { fprog prog; prog.filter = g_filters; int prog_idx = 0; if (seize) { pid_seize (pid, quiet); return; } if (ptrace (PTRACE_SEIZE, pid, 0, 0) != 0) error_seize (pid, errno); ptrace (PTRACE_INTERRUPT, pid, 0, 0); waitpid (pid, NULL, 0); syscall_info info; ptrace (PTRACE_GET_SYSCALL_INFO, pid, sizeof (info), &info); uint32_t scmp_arch = info.arch; while (true) { prog.len = ptrace (PTRACE_SECCOMP_GET_FILTER, pid, prog_idx, prog.filter); if (prog.len != (unsigned short)-1) { print_prog (scmp_arch, &prog, stdout); prog_idx++; continue; } if (!error_get_filter (pid, errno)) break; } if (prog_idx == 0) info (M_NO_FILTER_FOUND, pid); ptrace (PTRACE_DETACH, pid, 0, 0); } ceccomp-4.0/src/utils/000077500000000000000000000000001514205130000147045ustar00rootroot00000000000000ceccomp-4.0/src/utils/arch_trans.c000066400000000000000000000100641514205130000171750ustar00rootroot00000000000000#include "utils/arch_trans.h" #include "lexical/token.h" #include #include #include static const uint32_t arch_pairs[] = { [ARCH_X86] = SCMP_ARCH_X86, [ARCH_I686] = SCMP_ARCH_X86, [ARCH_X86_64] = SCMP_ARCH_X86_64, [ARCH_X32] = SCMP_ARCH_X32, [ARCH_ARM] = SCMP_ARCH_ARM, [ARCH_AARCH64] = SCMP_ARCH_AARCH64, #if SCMP_VER_MAJOR >= 2 && SCMP_VER_MINOR >= 6 [ARCH_LOONGARCH64] = SCMP_ARCH_LOONGARCH64, [ARCH_M68K] = SCMP_ARCH_M68K, #else [ARCH_LOONGARCH64] = -1, [ARCH_M68K] = -1, #endif [ARCH_MIPSEL64N32] = SCMP_ARCH_MIPSEL64N32, [ARCH_MIPSEL64] = SCMP_ARCH_MIPSEL64, [ARCH_MIPSEL] = SCMP_ARCH_MIPSEL, [ARCH_MIPS64N32] = SCMP_ARCH_MIPS64N32, [ARCH_MIPS64] = SCMP_ARCH_MIPS64, [ARCH_MIPS] = SCMP_ARCH_MIPS, [ARCH_PARISC64] = SCMP_ARCH_PARISC64, [ARCH_PARISC] = SCMP_ARCH_PARISC, [ARCH_PPC64LE] = SCMP_ARCH_PPC64LE, [ARCH_PPC64] = SCMP_ARCH_PPC64, [ARCH_PPC] = SCMP_ARCH_PPC, [ARCH_S390X] = SCMP_ARCH_S390X, [ARCH_S390] = SCMP_ARCH_S390, [ARCH_RISCV64] = SCMP_ARCH_RISCV64, }; uint32_t internal_arch_to_scmp_arch (uint32_t internal_arch) { // ARCH_X86 = 0, so (arch >= ARCH_X86) is always true; if (internal_arch <= ARCH_RISCV64) return arch_pairs[internal_arch]; return -1; } uint32_t scmp_arch_to_internal_arch (uint32_t scmp_arch) { switch (scmp_arch) { // clang-format off case SCMP_ARCH_X86: return ARCH_X86; case SCMP_ARCH_X86_64: return ARCH_X86_64; case SCMP_ARCH_X32: return ARCH_X32; case SCMP_ARCH_ARM: return ARCH_ARM; case SCMP_ARCH_AARCH64: return ARCH_AARCH64; #if SCMP_VER_MAJOR >= 2 && SCMP_VER_MINOR >= 6 case SCMP_ARCH_LOONGARCH64: return ARCH_LOONGARCH64; case SCMP_ARCH_M68K: return ARCH_M68K; #endif case SCMP_ARCH_MIPSEL64N32: return ARCH_MIPSEL64N32; case SCMP_ARCH_MIPSEL64: return ARCH_MIPSEL64; case SCMP_ARCH_MIPSEL: return ARCH_MIPSEL; case SCMP_ARCH_MIPS64N32: return ARCH_MIPS64N32; case SCMP_ARCH_MIPS64: return ARCH_MIPS64; case SCMP_ARCH_MIPS: return ARCH_MIPS; case SCMP_ARCH_PARISC64: return ARCH_PARISC64; case SCMP_ARCH_PARISC: return ARCH_PARISC; case SCMP_ARCH_PPC64LE: return ARCH_PPC64LE; case SCMP_ARCH_PPC64: return ARCH_PPC64; case SCMP_ARCH_PPC: return ARCH_PPC; case SCMP_ARCH_S390X: return ARCH_S390X; case SCMP_ARCH_S390: return ARCH_S390; case SCMP_ARCH_RISCV64: return ARCH_RISCV64; // clang-format on } return -1; } #define MAYBE_MATCH_ARCH(arch) \ if (!strncmp (str, token_pairs[arch].start, token_pairs[arch].len)) \ return arch; token_type str_to_internal_arch (const char *str) { switch (*str) { case 'i': MAYBE_MATCH_ARCH (ARCH_X86); MAYBE_MATCH_ARCH (ARCH_I686); break; case 'x': MAYBE_MATCH_ARCH (ARCH_X86_64); MAYBE_MATCH_ARCH (ARCH_X32); break; case 'a': MAYBE_MATCH_ARCH (ARCH_ARM); MAYBE_MATCH_ARCH (ARCH_AARCH64); break; case 'l': MAYBE_MATCH_ARCH (ARCH_LOONGARCH64); break; case 'm': MAYBE_MATCH_ARCH (ARCH_M68K); MAYBE_MATCH_ARCH (ARCH_MIPSEL64N32); MAYBE_MATCH_ARCH (ARCH_MIPSEL64); MAYBE_MATCH_ARCH (ARCH_MIPSEL); MAYBE_MATCH_ARCH (ARCH_MIPS64N32); MAYBE_MATCH_ARCH (ARCH_MIPS64); MAYBE_MATCH_ARCH (ARCH_MIPS); break; case 'p': MAYBE_MATCH_ARCH (ARCH_PARISC64); MAYBE_MATCH_ARCH (ARCH_PARISC); MAYBE_MATCH_ARCH (ARCH_PPC64LE); MAYBE_MATCH_ARCH (ARCH_PPC64); MAYBE_MATCH_ARCH (ARCH_PPC); break; case 's': MAYBE_MATCH_ARCH (ARCH_S390X); MAYBE_MATCH_ARCH (ARCH_S390); break; case 'r': MAYBE_MATCH_ARCH (ARCH_RISCV64); break; } return UNKNOWN; } uint32_t str_to_scmp_arch (const char *str) { token_type tk = str_to_internal_arch (str); if (tk == UNKNOWN) return -1; return internal_arch_to_scmp_arch (tk); } const string_t * scmp_arch_to_str (uint32_t scmp_arch) { int32_t idx = scmp_arch_to_internal_arch (scmp_arch); if (idx == -1) return NULL; return &token_pairs[idx]; } ceccomp-4.0/src/utils/color.c000066400000000000000000000016501514205130000161700ustar00rootroot00000000000000#include "utils/color.h" #include #include #include #include bool color_enable = true; bool log_color_enable = true; void set_color (color_mode_t color, FILE *output) { if (color == ALWAYS) { color_enable = true; log_color_enable = true; } else if (color == NEVER) { color_enable = false; log_color_enable = false; } else if (color == AUTO) { if (isatty (fileno (output))) color_enable = true; else color_enable = false; if (isatty (STDERR_FILENO)) log_color_enable = true; else log_color_enable = false; } } static bool enable_stack[0x10]; // 0x10 is definitely enough; static uint32_t enable_sp = 0; void push_color (bool enable) { enable_stack[enable_sp++] = color_enable; color_enable = enable; } void pop_color (void) { color_enable = enable_stack[--enable_sp]; } ceccomp-4.0/src/utils/hash.c000066400000000000000000000022401514205130000157710ustar00rootroot00000000000000#include "utils/hash.h" #include #include #include #include #include #include "lib/a5hash-5.25.h" static uint64_t hash_hkey (hkey_t key) { #if __SIZEOF_POINTER__ == 8 return a5hash (key.start, key.len, 0); #else return a5hash32 (key.start, key.len, 0); #endif } static bool cmpr_hkey (hkey_t key1, hkey_t key2) { if (key1.len != key2.len) return false; return !memcmp (key1.start, key2.start, key1.len); } #define NAME str_table #define KEY_TY hkey_t #define VAL_TY uint16_t #define HASH_FN hash_hkey #define CMPR_FN cmpr_hkey #include "lib/verstable-2.2.1.h" str_table hash_table; int insert_key (hkey_t *key, uint16_t line_nr) { size_t prev_size = vt_size (&hash_table); str_table_itr itr = vt_get_or_insert (&hash_table, *key, line_nr); if (vt_is_end (itr)) return -1; if (prev_size == vt_size (&hash_table)) return 1; return 0; } uint16_t find_key (hkey_t *key) { str_table_itr itr = vt_get (&hash_table, *key); if (vt_is_end (itr)) return -1; return itr.data->val; } void init_table (void) { vt_init (&hash_table); } void free_table (void) { vt_cleanup (&hash_table); } ceccomp-4.0/src/utils/i18n.c000066400000000000000000000000411514205130000156220ustar00rootroot00000000000000#include "i18n.h" locale_t lc_c; ceccomp-4.0/src/utils/logger.c000066400000000000000000000034571514205130000163400ustar00rootroot00000000000000#include "utils/logger.h" #include "utils/color.h" #include #include #include #include #define LOG_CYAN(str) ((log_color_enable) ? (CYANCLR str CLR) : str) #define LOG_BLUE(str) ((log_color_enable) ? (BLUECLR str CLR) : str) #define LOG_YELLOW(str) ((log_color_enable) ? (YELLOWCLR str CLR) : str) #define LOG_RED(str) ((log_color_enable) ? (REDCLR str CLR) : str) #define DEBUG_PREFIX LOG_CYAN ("[DEBUG]: ") #define INFO LOG_BLUE ("[INFO]: ") #define WARN LOG_YELLOW ("[WARN]: ") #define ERR LOG_RED ("[ERROR]: ") void debug_print (const char *caller_func, const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf (stderr, DEBUG_PREFIX); #ifdef DEBUG fprintf (stderr, "in %s: ", caller_func); #else (void)caller_func; #endif vfprintf (stderr, fmt, args); putc ('\n', stderr); va_end (args); fflush (stderr); } void info_print (const char *caller_func, const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf (stderr, INFO); #ifdef DEBUG fprintf (stderr, "in %s: ", caller_func); #else (void)caller_func; #endif vfprintf (stderr, fmt, args); putc ('\n', stderr); va_end (args); fflush (stderr); } void warn_print (const char *caller_func, const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf (stderr, WARN); #ifdef DEBUG fprintf (stderr, "in %s: ", caller_func); #else (void)caller_func; #endif vfprintf (stderr, fmt, args); putc ('\n', stderr); va_end (args); fflush (stderr); } void error_print (const char *caller_func, const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf (stderr, ERR); #ifdef DEBUG fprintf (stderr, "in %s: ", caller_func); #else (void)caller_func; #endif vfprintf (stderr, fmt, args); putc ('\n', stderr); va_end (args); exit (1); } ceccomp-4.0/src/utils/parse_args.c000066400000000000000000000130171514205130000172000ustar00rootroot00000000000000#include "utils/parse_args.h" #include "utils/arch_trans.h" #include "utils/error.h" #include "utils/logger.h" #include #include #include #include #include #include #include static bool stop_parse = false; static uint64_t fail_fast_strtoull (const char *restrict num, const char *restrict error_msg) { char *end; errno = 0; uint64_t result = strtoull (num, &end, 0); if (*end != '\0' && errno) error ("%s", error_msg); return result; } static FILE * fail_fast_fopen (const char *restrict filename, const char *restrict mode) { if (!strcmp (filename, "-")) { if (*mode == 'r') return stdin; else return stdout; } FILE *fp = fopen (filename, mode); if (fp == NULL) error (M_UNABLE_OPEN_FILE, filename, strerror (errno)); return fp; } static subcommand_t parse_subcommand (const char *arg) { if (!strcmp (arg, "asm")) return ASM_MODE; else if (!strcmp (arg, "disasm")) return DISASM_MODE; else if (!strcmp (arg, "emu")) return EMU_MODE; else if (!strcmp (arg, "trace")) return TRACE_MODE; else if (!strcmp (arg, "probe")) return PROBE_MODE; else if (!strcmp (arg, "version")) return VERSION_MODE; else if (!strcmp (arg, "help")) return HELP_MODE; else return HELP_ABNORMAL; } static color_mode_t parse_color_mode (const char *arg) { if (!strcmp (arg, "always")) return ALWAYS; else if (!strcmp (arg, "auto")) return AUTO; else if (!strcmp (arg, "never")) return NEVER; else error ("%s: %s", M_INVALID_COLOR_MODE, arg); } static print_mode_t parse_print_mode (const char *arg) { if (!strcmp (arg, "hexfmt")) return HEXFMT; else if (!strcmp (arg, "hexline")) return HEXLINE; else if (!strcmp (arg, "raw")) return RAW; else error ("%s: %s", M_INVALID_FMT_MODE, arg); } static int parse_asm (asm_arg_t *args, int key, const char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_ARG: if (state->arg_num == 1) args->text_file = fail_fast_fopen (arg, "r"); return 0; case 'a': args->scmp_arch = str_to_scmp_arch (arg); return 0; case 'f': args->mode = parse_print_mode (arg); return 0; } return 0; } static int parse_disasm (disasm_arg_t *args, int key, const char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_ARG: if (state->arg_num == 1) args->raw_file = fail_fast_fopen (arg, "r"); return 0; case 'a': args->scmp_arch = str_to_scmp_arch (arg); return 0; } return 0; } static int parse_emu (emu_arg_t *args, int key, const char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_ARG: if (state->arg_num == 1) args->text_file = fail_fast_fopen (arg, "r"); else if (state->arg_num == 2) args->sys_name = arg; else if (state->arg_num >= 3 && state->arg_num <= 8) args->args[state->arg_num - 3] = fail_fast_strtoull (arg, M_INVALID_NUMBER); else if (state->arg_num == 9) args->ip = fail_fast_strtoull (arg, M_INVALID_NUMBER); return 0; case 'a': args->scmp_arch = str_to_scmp_arch (arg); return 0; case 'q': args->quiet = true; return 0; } return 0; } static int parse_trace (trace_arg_t *args, int key, const char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_ARG: if (state->arg_num != 1 || args->mode != UNDECIDED) return 0; args->mode = TRACE_PROG; args->prog_idx = state->next - 1; stop_parse = true; return 0; case 'p': if (args->mode != UNDECIDED) return 0; args->mode = TRACE_PID; args->pid = fail_fast_strtoull (arg, M_INVALID_NUMBER); return 0; case 'o': if (args->mode != UNDECIDED) return 0; args->output_file = fail_fast_fopen (arg, "w+"); return 0; case 'q': args->quiet = true; return 0; case 's': args->seize = true; return 0; } return 0; } static int parse_probe (probe_arg_t *args, int key, const char *arg, struct argp_state *state) { switch (key) { case ARGP_KEY_ARG: if (state->arg_num != 1) return 0; args->prog_idx = state->next - 1; stop_parse = true; return 0; case 'o': args->output_file = fail_fast_fopen (arg, "w+"); return 0; case 'q': args->quiet = true; return 0; } return 0; } error_t parse_opt (int key, char *arg, struct argp_state *state) { ceccomp_arg_t *args = state->input; if (stop_parse) return 0; switch (key) { case ARGP_KEY_ARG: if (state->arg_num == 0) args->cmd = parse_subcommand (arg); break; case 'c': args->when = parse_color_mode (arg); return 0; case 'h': if (args->cmd == HELP_ABNORMAL) args->cmd = HELP_MODE; return 0; case 'u': if (args->cmd == HELP_ABNORMAL) args->cmd = HELP_MODE; return 0; } if (args->cmd == ASM_MODE) return parse_asm (args->asm_arg, key, arg, state); else if (args->cmd == DISASM_MODE) return parse_disasm (args->disasm_arg, key, arg, state); else if (args->cmd == EMU_MODE) return parse_emu (args->emu_arg, key, arg, state); else if (args->cmd == TRACE_MODE) return parse_trace (args->trace_arg, key, arg, state); else if (args->cmd == PROBE_MODE) return parse_probe (args->probe_arg, key, arg, state); return 0; } ceccomp-4.0/src/utils/proc_status.c000066400000000000000000000024061514205130000174200ustar00rootroot00000000000000#include "utils/proc_status.h" #include "main.h" #include #include #include #include #include #include #define SECCOMP "Seccomp:" // the sizeof simply including appending \t or space #define SECCOMP_OFFSET (sizeof (SECCOMP)) #define KTHREAD "Kthread:" #define KTHREAD_OFFSET (sizeof (KTHREAD)) #define TRACER_PID "TracerPid:" #define TRACER_PID_OFFSET (sizeof (TRACER_PID)) static long access_proc_with_key (pid_t pid, const char *comparator, size_t strsize) { long value = PROCFS_ERROR; char buf[0x40]; char *end; snprintf (buf, 0x40, "/proc/%d/status", pid); FILE *f = fopen (buf, "r"); if (f == NULL) return PROCFS_ERROR; while (fgets (buf, 0x40, f)) if (STARTWITH (buf, comparator)) { value = strtoull (buf + strsize, &end, 10); if (end == buf + strsize) value = PROCFS_ERROR; break; } fclose (f); return value; } seccomp_mode get_proc_seccomp (pid_t pid) { return access_proc_with_key (pid, SECCOMP, SECCOMP_OFFSET); } kthread_mode is_proc_kthread (pid_t pid) { return access_proc_with_key (pid, KTHREAD, KTHREAD_OFFSET); } pid_t get_tracer_pid (pid_t pid) { return access_proc_with_key (pid, TRACER_PID, TRACER_PID_OFFSET); } ceccomp-4.0/src/utils/read_source.c000066400000000000000000000112111514205130000173370ustar00rootroot00000000000000#define _GNU_SOURCE #include "utils/read_source.h" #include "utils/error.h" #include "utils/logger.h" #include #include #include #include #include #include #include #include #include #include #include #define GROW_LEN 0x4000 #define MAX_LINE_LEN 0x180 #define MAX_FILE_LEN 0x100000 // 1MiB typedef enum { UNKNOWN, UNIX, WINDOWS, MACOS, } file_type_t; static file_type_t file_type = UNKNOWN; static char *source = NULL; static uint32_t current = 0; static uint32_t map_len = 0; static void clear_color (char *text, uint32_t line_len) { char *cursor = memchr (text, '\x1b', line_len); if (!cursor) return; char *top = text + line_len; // excluding char *colorstart = NULL; char *clear = cursor; for (; cursor < top; cursor++) { if (!colorstart && *cursor != '\x1b') *clear++ = *cursor; else if (!colorstart && *cursor == '\x1b') colorstart = cursor; else if (colorstart && *cursor == 'm') colorstart = NULL; // else skip } memset (clear, '\0', top - clear); } static char detect_file_type (void) { char lf = '\n'; char *line_break = memchr (source, lf, current); if (!line_break) { if (memchr (source, '\r', current)) // perhaps the file is from macos? { file_type = MACOS; return '\r'; } else error ("%s", M_FOUND_SUS_NO_LF); } else { if (line_break != source && *(line_break - 1) == '\r') file_type = WINDOWS; else file_type = UNIX; } return '\n'; } static unsigned process_source (void) { register char lf = detect_file_type (); char *line_break; char *line_start = source; char *top = source + current; uint32_t line_nr = 1; while (true) { if (line_start + MAX_LINE_LEN <= top) { line_break = memchr (line_start, lf, MAX_LINE_LEN); if (!line_break) error (M_FOUND_SUS_LINE, line_nr, MAX_LINE_LEN); } else { // the rest space is less than MAX_LINE_LEN line_break = memchr (line_start, lf, top - line_start); if (!line_break) break; } if (file_type == WINDOWS) memcpy (line_break - 1, "\n", 2); else *line_break = '\n'; clear_color (line_start, line_break - line_start + 1); line_nr++; line_start = line_break + 1; } if (line_nr >= 4096) error ("%s", M_LINES_TOO_MANY); if (top == line_start) return line_nr; clear_color (line_start, top - line_start + 1); return line_nr + 1; } static void init_map (void) { source = mmap (NULL, GROW_LEN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (source == MAP_FAILED) error ("mmap: %s", strerror (errno)); map_len = GROW_LEN; } static void increase_map (void) { if (source == NULL) { init_map (); return; } if (current + GROW_LEN <= map_len) return; source = mremap (source, map_len, map_len + GROW_LEN, MREMAP_MAYMOVE); if (source == MAP_FAILED) error ("mremap: %s", strerror (errno)); map_len += GROW_LEN; } unsigned init_source (FILE *read_fp) { uint32_t read_len = 0; int fd = fileno (read_fp); // give compiler some hint do { increase_map (); read_len = read (fd, source + current, GROW_LEN); if (read_len == (uint32_t)-1) error (M_READ_FAIL, strerror (errno)); current += read_len; if (current > MAX_FILE_LEN) error ("%s", M_FILE_TOO_LARGE); } while (read_len > 0); // reading via char device may get less than GROW_LEN const char *found0; if ((found0 = memchr (source, '\0', current))) error (M_FOUND_SUS_ZERO, (unsigned long)(found0 - source)); if (current % GROW_LEN == 0 || current % GROW_LEN > GROW_LEN - 0x20) increase_map (); // scanner expect a trailing \n, which may increase map #ifdef PR_SET_VMA prctl (PR_SET_VMA, PR_SET_VMA_ANON_NAME, source, map_len, "asm source"); #endif return process_source (); } void free_source (void) { munmap (source, map_len); } char * next_line (void) { static uint32_t cursor = 0; if (cursor >= current) return NULL; char *read_ptr = source + cursor; char *line_break = memchr (read_ptr, '\n', current - cursor); if (!line_break) { // meet eof source[current] = '\n'; cursor = current; return read_ptr; } char *top = source + current; // give compiler some hint do line_break++; while (line_break < top && (*line_break == '\0')); cursor = line_break - source; return read_ptr; } ceccomp-4.0/src/utils/str_pile.c000066400000000000000000000024161514205130000166740ustar00rootroot00000000000000#include "utils/str_pile.h" #include "main.h" #include #include #include #include #include #include #include static char *str_pile; static char *pile_top; static char *pile_cursor; bool init_pile (size_t init_size) { size_t aligned = (init_size + 0xfff) & ~0xfff; str_pile = mmap (NULL, aligned, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (!str_pile) return false; #ifdef PR_SET_VMA prctl (PR_SET_VMA, PR_SET_VMA_ANON_NAME, str_pile, aligned, "str pile"); #endif pile_cursor = str_pile; pile_top = str_pile + aligned; return true; } string_t persist_object (const char *sysname, const string_t *arch) { uint32_t syslen = strlen (sysname); uint32_t size; if (arch) size = arch->len + 1 + syslen; // arch.sysname else size = syslen; // sysname assert (pile_cursor + size <= pile_top); if (arch) { memcpy (pile_cursor, arch->start, arch->len); pile_cursor += arch->len; *pile_cursor++ = '.'; } memcpy (pile_cursor, sysname, syslen); pile_cursor += syslen; return (string_t){ .start = pile_cursor - size, .len = size }; } void free_pile (void) { assert (!munmap (str_pile, pile_top - str_pile)); } ceccomp-4.0/src/utils/vector.c000066400000000000000000000023241514205130000163530ustar00rootroot00000000000000#include "utils/vector.h" #include "main.h" #include "utils/logger.h" #include #include #include #include #include void * reallocate (void *p, size_t new_size) { if (new_size == 0) { free (p); return NULL; } p = realloc (p, new_size); if (p == NULL) error ("realloc: %s", strerror (errno)); return p; } #define UPDATE_VECTOR \ v->data = reallocate (v->data, v->capacity * v->elem_size); void init_vector (vector_t *v, size_t elem_size, size_t initial_capacity) { v->count = 0; v->capacity = initial_capacity ? initial_capacity : 0x10; v->elem_size = elem_size; v->data = NULL; UPDATE_VECTOR; } void free_vector (vector_t *v) { v->count = 0; v->capacity = 0; v->elem_size = 0; UPDATE_VECTOR; } void * push_vector (vector_t *v, void *elem) { if (UNLIKELY (v->count >= v->capacity)) { v->capacity *= 2; UPDATE_VECTOR; } void *dst = (uint8_t *)v->data + v->count * v->elem_size; memcpy (dst, elem, v->elem_size); v->count++; return dst; } void * get_vector (vector_t *v, uint32_t idx) { return (uint8_t *)v->data + idx * v->elem_size; } ceccomp-4.0/test/000077500000000000000000000000001514205130000137345ustar00rootroot00000000000000ceccomp-4.0/test/big_endian_cases/000077500000000000000000000000001514205130000171715ustar00rootroot00000000000000ceccomp-4.0/test/big_endian_cases/s390x.bpf000066400000000000000000000000601514205130000205440ustar00rootroot00000000000000  ceccomp-4.0/test/big_endian_cases/s390x.disasm000066400000000000000000000006341514205130000212640ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x02 0x80000016 if ($A != s390x) goto L0005 L0003: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0004: 0x15 0x01 0x00 0x00000096 if ($A == mlock) goto L0006 L0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0006: 0x06 0x00 0x00 0x00000000 return KILL #--------------------------------- ceccomp-4.0/test/big_endian_cases/s390x.hexfmt000066400000000000000000000003301514205130000212700ustar00rootroot00000000000000"\x00\x20\x00\x00\x00\x00\x00\x04", "\x00\x15\x00\x02\x80\x00\x00\x16", "\x00\x20\x00\x00\x00\x00\x00\x00", "\x00\x15\x01\x00\x00\x00\x00\x96", "\x00\x06\x00\x00\x7f\xff\x00\x00", "\x00\x06\x00\x00\x00\x00\x00\x00", ceccomp-4.0/test/big_endian_cases/s390x.text000066400000000000000000000001751514205130000207700ustar00rootroot00000000000000$A = $arch if ($A != s390x) goto allow $A = $syscall_nr if ($A == mlock) goto forbid allow: return ALLOW forbid: return KILL ceccomp-4.0/test/big_endian_cases/s390x.text.mmap000066400000000000000000000002001514205130000217060ustar00rootroot00000000000000$A = $arch if ($A != s390x) goto allow $A = $syscall_nr if ($A == mlock) goto forbid allow: return ALLOW #... 2 line(s) skipped ceccomp-4.0/test/bpf/000077500000000000000000000000001514205130000145035ustar00rootroot00000000000000ceccomp-4.0/test/bpf/CONFidence-2017-amigo.bpf000066400000000000000000000014201514205130000205470ustar00rootroot00000000000000 @ 8 <17`7s3`` ;-%#~~998  ```  ```  ```  $xV4```ceccomp-4.0/test/bpf/DEF-CON-2020-bdooos.bpf000066400000000000000000000002301514205130000200460ustar00rootroot00000000000000    ? @ I ^bceccomp-4.0/test/bpf/chromium.bpf000066400000000000000000000113401514205130000170160ustar00rootroot00000000000000 >  E@ %J%$%/% M%%%%BL5%O55=N%@%J%K59L5I%>56?5<%%%%)%*5.5&%!5-"5 %%%5(5'%5%5$%%%%%y5x5%5u5K%%%q55%5m5l% %%%55 %%5Ǻ%%%5^ì5%55 %%L%v% %%53%"5O%5M5%z%~%I55G|%x5y5Dw%h%n%q%@s5?t5o%k5m5;i%b%e%f5g5c%N54`53M% % 8%?%H5I#5ّJ5+@%<%>q5'9w%,%3%556{51%'5(5$% %%%55z%5%%%% 5 5 %55 , (E (&  E \ $ E EU  E   E   E E@  E E8 z Ex y s Eq r l Ej k e Ec d ^ E\ ] W EU V P EN O I EG HK A E? E@ : E8 T8 2 E0 T0 * E( T( " E T   E T  E T   E   E   E   E   E AMVS  E amaY  E   E   E   E   E   E   E   E   E    E   E   E   E   E  { Ey { t Er   m Ek n  $`l $j Ehgc ` E^ `  [= EX TAUA N EL O $G EE GH* @ E> @A $9 E7 9 $2 E0 2 $+ E) + $$ E" $ $ E   T  T  b@ $ E E , (E (E̗ceccomp-4.0/test/bpf/gctf-2019-quals-caas.bpf000066400000000000000000000007701514205130000205440ustar00rootroot00000000000000 <> 5:@8765 43 2!1#0*/+./-1,<+8 ) &'  ) $ "   $      $   , ( " 4 0 < 8ceccomp-4.0/test/bpf/ld_alu_misc.bpf000066400000000000000000000002601514205130000174450ustar00rootroot00000000000000   8 T $$7`ceccomp-4.0/test/bpf/libseccomp.bpf000066400000000000000000000001301514205130000173060ustar00rootroot00000000000000 > 5@B;H<ceccomp-4.0/test/bpf/multi_arch.bpf000066400000000000000000000003001514205130000173140ustar00rootroot00000000000000 > 5@+21@     \H(@ xceccomp-4.0/test/bpf/twctf-2016-diary.bpf000066400000000000000000000002201514205130000200110ustar00rootroot00000000000000 ;89:UBceccomp-4.0/test/bpf/x32.bpf000066400000000000000000000001401514205130000156030ustar00rootroot00000000000000  @ %@@@@ @ ceccomp-4.0/test/conftest.py000066400000000000000000000007741514205130000161430ustar00rootroot00000000000000from types import SimpleNamespace import pytest @pytest.fixture def errns() -> SimpleNamespace: return SimpleNamespace() # hook pytest report to print stderr @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == 'call' and report.failed: ns = item.funcargs.get('errns') if hasattr(ns, 'stderr'): report.sections.append(('Process Standard Error', ns.stderr)) ceccomp-4.0/test/dyn_log/000077500000000000000000000000001514205130000153675ustar00rootroot00000000000000ceccomp-4.0/test/dyn_log/probe.log000066400000000000000000000004671514205130000172100ustar00rootroot00000000000000open -> ALLOW openat -> ALLOW read -> ALLOW write -> ALLOW execve -> ERRNO(1) execveat -> ALLOW mmap -> ALLOW mprotect -> ALLOW sendfile -> ALLOW ptrace -> ALLOW fork -> ALLOW ceccomp-4.0/test/dyn_log/trace.log000066400000000000000000000006071514205130000171730ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0002: 0x15 0x01 0x00 0x0000003b if ($A == execve) goto L0004 L0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0004: 0x06 0x00 0x00 0x00050001 return ERRNO(1) #--------------------------------- ceccomp-4.0/test/emu_result/000077500000000000000000000000001514205130000161205ustar00rootroot00000000000000ceccomp-4.0/test/emu_result/CONFidence-2017-amigo.accept000066400000000000000000000011211514205130000226520ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != i386) goto L0004 L0003: goto L0014 L0004: $A = $low_args[5] L0005: $mem[0x0] = $A L0006: $A = $high_args[5] L0007: $mem[0x1] = $A L0008: if ($A != 0x3133731) goto L0012 L0009: $A = $mem[0x0] L0010: if ($A == 0x33731337) goto L0013 L0011: $A = $mem[0x1] L0012: return KILL #... 87 line(s) skipped ceccomp-4.0/test/emu_result/CONFidence-2017-amigo.open000066400000000000000000000011211514205130000223540ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != i386) goto L0004 L0003: goto L0014 L0004: $A = $low_args[5] L0005: $mem[0x0] = $A L0006: $A = $high_args[5] L0007: $mem[0x1] = $A L0008: if ($A != 0x3133731) goto L0012 L0009: $A = $mem[0x0] L0010: if ($A == 0x33731337) goto L0013 L0011: $A = $mem[0x1] L0012: return KILL #... 87 line(s) skipped ceccomp-4.0/test/emu_result/CONFidence-2017-amigo.pipe000066400000000000000000000011211514205130000223500ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != i386) goto L0004 L0003: goto L0014 L0004: $A = $low_args[5] L0005: $mem[0x0] = $A L0006: $A = $high_args[5] L0007: $mem[0x1] = $A L0008: if ($A != 0x3133731) goto L0012 L0009: $A = $mem[0x0] L0010: if ($A == 0x33731337) goto L0013 L0011: $A = $mem[0x1] L0012: return KILL #... 87 line(s) skipped ceccomp-4.0/test/emu_result/DEF-CON-2020-bdooos.accept000066400000000000000000000020211514205130000221530ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != aarch64) goto L0019 L0003: $A = $syscall_nr L0004: if ($A == aarch64.ioctl) goto L0018 L0005: if ($A == aarch64.read) goto L0018 L0006: if ($A == aarch64.write) goto L0018 L0007: if ($A == aarch64.ppoll) goto L0018 L0008: if ($A == aarch64.exit_group) goto L0018 L0009: if ($A == aarch64.futex) goto L0018 L0010: if ($A == aarch64.sigaltstack) goto L0018 L0011: if ($A == aarch64.rt_sigaction) goto L0018 L0012: if ($A == aarch64.rt_sigreturn) goto L0018 L0013: if ($A == aarch64.sendto) goto L0018 L0014: if ($A == aarch64.recvfrom) goto L0018 L0015: if ($A == aarch64.setsockopt) goto L0018 L0016: if ($A == aarch64.munmap) goto L0018 L0017: return KILL_PROCESS L0018: return ALLOW L0019: return KILL #... 1 line(s) skipped ceccomp-4.0/test/emu_result/DEF-CON-2020-bdooos.open000066400000000000000000000020211514205130000216550ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != aarch64) goto L0019 L0003: $A = $syscall_nr L0004: if ($A == aarch64.ioctl) goto L0018 L0005: if ($A == aarch64.read) goto L0018 L0006: if ($A == aarch64.write) goto L0018 L0007: if ($A == aarch64.ppoll) goto L0018 L0008: if ($A == aarch64.exit_group) goto L0018 L0009: if ($A == aarch64.futex) goto L0018 L0010: if ($A == aarch64.sigaltstack) goto L0018 L0011: if ($A == aarch64.rt_sigaction) goto L0018 L0012: if ($A == aarch64.rt_sigreturn) goto L0018 L0013: if ($A == aarch64.sendto) goto L0018 L0014: if ($A == aarch64.recvfrom) goto L0018 L0015: if ($A == aarch64.setsockopt) goto L0018 L0016: if ($A == aarch64.munmap) goto L0018 L0017: return KILL_PROCESS L0018: return ALLOW L0019: return KILL #... 1 line(s) skipped ceccomp-4.0/test/emu_result/DEF-CON-2020-bdooos.pipe000066400000000000000000000020211514205130000216510ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != aarch64) goto L0019 L0003: $A = $syscall_nr L0004: if ($A == aarch64.ioctl) goto L0018 L0005: if ($A == aarch64.read) goto L0018 L0006: if ($A == aarch64.write) goto L0018 L0007: if ($A == aarch64.ppoll) goto L0018 L0008: if ($A == aarch64.exit_group) goto L0018 L0009: if ($A == aarch64.futex) goto L0018 L0010: if ($A == aarch64.sigaltstack) goto L0018 L0011: if ($A == aarch64.rt_sigaction) goto L0018 L0012: if ($A == aarch64.rt_sigreturn) goto L0018 L0013: if ($A == aarch64.sendto) goto L0018 L0014: if ($A == aarch64.recvfrom) goto L0018 L0015: if ($A == aarch64.setsockopt) goto L0018 L0016: if ($A == aarch64.munmap) goto L0018 L0017: return KILL_PROCESS L0018: return ALLOW L0019: return KILL #... 1 line(s) skipped ceccomp-4.0/test/emu_result/chromium.accept000066400000000000000000000634661514205130000211430ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A == x86_64) goto L0004 L0003: return TRAP(10) L0004: $A = $syscall_nr L0005: if !($A & 0x40000000) goto L0007 L0006: return TRAP(9) L0007: if ($A <= sched_get_priority_max) goto L0082 L0008: if ($A <= pselect6) goto L0045 L0009: if ($A <= name_to_handle_at) goto L0028 L0010: if ($A <= io_pgetevents) goto L0020 L0011: if ($A <= close_range) goto L0017 L0012: if ($A <= process_madvise) goto L0016 L0013: if ($A <= mseal) goto L0015 L0014: if ($A <= setxattrat) goto L0081 L0015: goto L0604 L0016: if ($A >= faccessat2) goto L0171, else goto L0272 L0017: if ($A <= uretprobe) goto L0019 L0018: if ($A >= pidfd_open) goto L0172, else goto L0272 L0019: if ($A >= rseq) goto L0081, else goto L0272 L0020: if ($A <= kexec_file_load) goto L0025 L0021: if ($A <= pkey_alloc) goto L0024 L0022: if ($A <= pkey_free) goto L0173 L0023: if ($A >= statx) goto L0164, else goto L0081 L0024: if ($A >= pkey_mprotect) goto L0159, else goto L0272 L0025: if ($A <= getrandom) goto L0027 L0026: if ($A >= memfd_create) goto L0081, else goto L0180 L0027: if ($A >= renameat2) goto L0171, else goto L0272 L0028: if ($A <= timerfd_settime) goto L0037 L0029: if ($A <= pipe2) goto L0034 L0030: if ($A <= rt_tgsigqueueinfo) goto L0033 L0031: if ($A <= perf_event_open) goto L0194 L0032: if ($A >= prlimit64) goto L0187, else goto L0272 L0033: if ($A >= inotify_init1) goto L0272, else goto L0201 L0034: if ($A <= signalfd4) goto L0036 L0035: if ($A >= eventfd2) goto L0081, else goto L0272 L0036: if ($A >= accept4) goto L0171, else goto L0272 L0037: if ($A <= utimensat) goto L0042 L0038: if ($A <= signalfd) goto L0041 L0039: if ($A <= eventfd) goto L0272 L0040: if ($A >= fallocate) goto L0171, else goto L0081 L0041: if ($A >= epoll_pwait) goto L0081, else goto L0171 L0042: if ($A <= set_robust_list) goto L0044 L0043: if ($A >= get_robust_list) goto L0272, else goto L0081 L0044: if ($A >= unshare) goto L0272, else goto L0081 L0045: if ($A <= lookup_dcookie) goto L0063 L0046: if ($A <= exit_group) goto L0055 L0047: if ($A <= waitid) goto L0052 L0048: if ($A <= openat) goto L0051 L0049: if ($A <= newfstatat) goto L0171 L0050: if ($A >= unlinkat) goto L0171, else goto L0208 L0051: if ($A >= add_key) goto L0272, else goto L0081 L0052: if ($A <= utimes) goto L0054 L0053: if ($A >= vserver) goto L0272, else goto L0171 L0054: if ($A >= tgkill) goto L0130, else goto L0081 L0055: if ($A <= set_tid_address) goto L0060 L0056: if ($A <= semtimedop) goto L0059 L0057: if ($A <= fadvise64) goto L0171 L0058: if ($A >= clock_gettime) goto L0209, else goto L0272 L0059: if ($A >= restart_syscall) goto L0081, else goto L0272 L0060: if ($A <= epoll_ctl_old) goto L0062 L0061: if ($A >= getdents64) goto L0171, else goto L0272 L0062: if ($A >= epoll_create) goto L0081, else goto L0171 L0063: if ($A <= create_module) goto L0073 L0064: if ($A <= time) goto L0069 L0065: if ($A <= sched_setaffinity) goto L0068 L0066: if ($A <= sched_getaffinity) goto L0272 L0067: if ($A >= set_thread_area) goto L0272, else goto L0090 L0068: if ($A >= futex) goto L0273, else goto L0081 L0069: if ($A <= readahead) goto L0072 L0070: if ($A <= tkill) goto L0272 L0071: goto L0507 L0072: if ($A >= gettid) goto L0081, else goto L0272 L0073: if ($A <= prctl) goto L0078 L0074: if ($A <= setrlimit) goto L0077 L0075: if ($A <= chroot) goto L0081 L0076: if ($A >= iopl) goto L0171, else goto L0272 L0077: if ($A >= arch_prctl) goto L0272, else goto L0329 L0078: if ($A <= mlock) goto L0080 L0079: if ($A >= mlockall) goto L0272, else goto L0081 L0080: if ($A >= sched_rr_get_interval) goto L0272 L0081: goto L0603 L0082: if ($A <= truncate) goto L0120 L0083: if ($A <= getresuid) goto L0103 L0084: if ($A <= rt_sigqueueinfo) goto L0095 L0085: if ($A <= ustat) goto L0092 L0086: if ($A <= getpriority) goto L0091 L0087: if ($A >= sched_setparam) goto L0089 L0088: goto L0396 L0089: if ($A <= sched_getparam) goto L0272 L0090: goto L0381 L0091: if ($A >= fstatfs) goto L0272, else goto L0171 L0092: if ($A <= utime) goto L0094 L0093: if ($A >= personality) goto L0272, else goto L0171 L0094: if ($A >= sigaltstack) goto L0344, else goto L0272 L0095: if ($A <= setfsuid) goto L0100 L0096: if ($A <= capset) goto L0099 L0097: if ($A <= rt_sigpending) goto L0171 L0098: if ($A >= rt_sigtimedwait) goto L0344, else goto L0272 L0099: if ($A >= getsid) goto L0344, else goto L0171 L0100: if ($A <= getresgid) goto L0102 L0101: if ($A >= getpgid) goto L0272, else goto L0344 L0102: if ($A >= setresgid) goto L0171, else goto L0344 L0103: if ($A <= getgid) goto L0112 L0104: if ($A <= getppid) goto L0109 L0105: if ($A <= setreuid) goto L0108 L0106: if ($A <= getgroups) goto L0171 L0107: if ($A >= setgroups) goto L0171, else goto L0344 L0108: if ($A >= getpgrp) goto L0272, else goto L0344 L0109: if ($A <= geteuid) goto L0111 L0110: if ($A >= setpgid) goto L0272, else goto L0344 L0111: if ($A >= setuid) goto L0171, else goto L0344 L0112: if ($A <= getrusage) goto L0117 L0113: if ($A <= ptrace) goto L0116 L0114: if ($A <= getuid) goto L0272 L0115: if ($A >= syslog) goto L0272, else goto L0344 L0116: if ($A >= sysinfo) goto L0344, else goto L0272 L0117: if ($A <= getdents) goto L0119 L0118: if ($A >= gettimeofday) goto L0344, else goto L0171 L0119: if ($A >= ftruncate) goto L0344, else goto L0171 L0120: if ($A <= dup) goto L0143 L0121: if ($A <= clone) goto L0133 L0122: if ($A <= uname) goto L0128 L0123: if ($A <= fcntl) goto L0127 L0124: if ($A >= flock) goto L0126 L0125: goto L0417 L0126: if ($A >= fsync) goto L0344, else goto L0272 L0127: if ($A >= semget) goto L0171, else goto L0344 L0128: if ($A <= exit) goto L0131 L0129: if ($A <= kill) goto L0344 L0130: goto L0500 L0131: if ($A >= fork) goto L0171 L0132: goto L0508 L0133: if ($A <= sendto) goto L0140 L0134: if ($A <= getsockname) goto L0139 L0135: if ($A <= socketpair) goto L0272 L0136: if ($A >= setsockopt) goto L0138 L0137: goto L0532 L0138: goto L0518 L0139: if ($A >= bind) goto L0171, else goto L0344 L0140: if ($A <= getpid) goto L0142 L0141: if ($A >= sendfile) goto L0171, else goto L0344 L0142: if ($A >= getitimer) goto L0272, else goto L0344 L0143: if ($A <= munmap) goto L0154 L0144: if ($A <= pipe) goto L0150 L0145: if ($A <= mincore) goto L0149 L0146: if ($A <= madvise) goto L0344 L0147: if ($A >= shmget) goto L0171 L0148: goto L0539 L0149: if ($A >= msync) goto L0272, else goto L0344 L0150: if ($A <= pread64) goto L0152 L0151: if ($A >= access) goto L0171, else goto L0344 L0152: if ($A <= ioctl) goto L0344 L0153: goto L0574 L0154: if ($A <= fstat) goto L0161 L0155: if ($A <= poll) goto L0160 L0156: if ($A <= mmap) goto L0344 L0157: if ($A >= mprotect) goto L0159 L0158: goto L0594 L0159: goto L0587 L0160: if ($A >= lstat) goto L0171, else goto L0344 L0161: if ($A <= close) goto L0163 L0162: if ($A >= stat) goto L0171, else goto L0344 L0163: if ($A >= open) goto L0171, else goto L0344 L0164: $A = $high_args[3] L0165: if ($A == 0x0) goto L0169 L0166: if ($A != 0xffffffff) goto L0341 L0167: $A = $low_args[3] L0168: if !($A & 0x80000000) goto L0341 L0169: $A = $low_args[3] L0170: if ($A == 0x7ff) goto L0172 L0171: goto L0602 L0172: return ERRNO(38) L0173: $A = $high_args[0] L0174: if ($A == 0x0) goto L0178 L0175: if ($A != 0xffffffff) goto L0341 L0176: $A = $low_args[0] L0177: if !($A & 0x80000000) goto L0341 L0178: $A = $low_args[0] L0179: if ($A == 0x0) goto L0344, else goto L0272 L0180: $A = $high_args[2] L0181: if ($A == 0x0) goto L0185 L0182: if ($A != 0xffffffff) goto L0341 L0183: $A = $low_args[2] L0184: if !($A & 0x80000000) goto L0341 L0185: $A = $low_args[2] L0186: if ($A & 0xfffffffa) goto L0272, else goto L0344 L0187: $A = $high_args[0] L0188: if ($A == 0x0) goto L0192 L0189: if ($A != 0xffffffff) goto L0341 L0190: $A = $low_args[0] L0191: if !($A & 0x80000000) goto L0341 L0192: $A = $low_args[0] L0193: if ($A == 0x0) goto L0344 L0194: $A = $high_args[0] L0195: if ($A == 0x0) goto L0199 L0196: if ($A != 0xffffffff) goto L0341 L0197: $A = $low_args[0] L0198: if !($A & 0x80000000) goto L0341 L0199: $A = $low_args[0] L0200: goto L0416 L0201: $A = $high_args[1] L0202: if ($A == 0x0) goto L0206 L0203: if ($A != 0xffffffff) goto L0341 L0204: $A = $low_args[1] L0205: if !($A & 0x80000000) goto L0341 L0206: $A = $low_args[1] L0207: if ($A & 0xfff7b7ff) goto L0272, else goto L0344 L0208: return TRAP(8) L0209: $A = $high_args[0] L0210: if ($A == 0x0) goto L0214 L0211: if ($A != 0xffffffff) goto L0341 L0212: $A = $low_args[0] L0213: if !($A & 0x80000000) goto L0341 L0214: $A = $low_args[0] L0215: if ($A & 0x80000000) goto L0272 L0216: $A = $high_args[0] L0217: if ($A == 0x0) goto L0221 L0218: if ($A != 0xffffffff) goto L0341 L0219: $A = $low_args[0] L0220: if !($A & 0x80000000) goto L0341 L0221: $A = $low_args[0] L0222: if ($A == 0x7) goto L0344 L0223: $A = $high_args[0] L0224: if ($A == 0x0) goto L0228 L0225: if ($A != 0xffffffff) goto L0341 L0226: $A = $low_args[0] L0227: if !($A & 0x80000000) goto L0341 L0228: $A = $low_args[0] L0229: if ($A == 0x1) goto L0344 L0230: $A = $high_args[0] L0231: if ($A == 0x0) goto L0235 L0232: if ($A != 0xffffffff) goto L0341 L0233: $A = $low_args[0] L0234: if !($A & 0x80000000) goto L0341 L0235: $A = $low_args[0] L0236: if ($A == 0x6) goto L0344 L0237: $A = $high_args[0] L0238: if ($A == 0x0) goto L0242 L0239: if ($A != 0xffffffff) goto L0341 L0240: $A = $low_args[0] L0241: if !($A & 0x80000000) goto L0341 L0242: $A = $low_args[0] L0243: if ($A == 0x4) goto L0344 L0244: $A = $high_args[0] L0245: if ($A == 0x0) goto L0249 L0246: if ($A != 0xffffffff) goto L0341 L0247: $A = $low_args[0] L0248: if !($A & 0x80000000) goto L0341 L0249: $A = $low_args[0] L0250: if ($A == 0x2) goto L0344 L0251: $A = $high_args[0] L0252: if ($A == 0x0) goto L0256 L0253: if ($A != 0xffffffff) goto L0341 L0254: $A = $low_args[0] L0255: if !($A & 0x80000000) goto L0341 L0256: $A = $low_args[0] L0257: if ($A == 0x0) goto L0344 L0258: $A = $high_args[0] L0259: if ($A == 0x0) goto L0263 L0260: if ($A != 0xffffffff) goto L0341 L0261: $A = $low_args[0] L0262: if !($A & 0x80000000) goto L0341 L0263: $A = $low_args[0] L0264: if ($A == 0x5) goto L0344 L0265: $A = $high_args[0] L0266: if ($A == 0x0) goto L0270 L0267: if ($A != 0xffffffff) goto L0341 L0268: $A = $low_args[0] L0269: if !($A & 0x80000000) goto L0341 L0270: $A = $low_args[0] L0271: if ($A == 0x3) goto L0344 L0272: goto L0604 L0273: $A = $high_args[1] L0274: if ($A == 0x0) goto L0278 L0275: if ($A != 0xffffffff) goto L0341 L0276: $A = $low_args[1] L0277: if !($A & 0x80000000) goto L0341 L0278: $A = $low_args[1] L0279: if !($A & 0xfffffe7f) goto L0344 L0280: $A = $high_args[1] L0281: if ($A == 0x0) goto L0285 L0282: if ($A != 0xffffffff) goto L0341 L0283: $A = $low_args[1] L0284: if !($A & 0x80000000) goto L0341 L0285: $A = $low_args[1] L0286: $A &= 0xfffffe7f L0287: if ($A == 0x1) goto L0344 L0288: $A = $high_args[1] L0289: if ($A == 0x0) goto L0293 L0290: if ($A != 0xffffffff) goto L0341 L0291: $A = $low_args[1] L0292: if !($A & 0x80000000) goto L0341 L0293: $A = $low_args[1] L0294: $A &= 0xfffffe7f L0295: if ($A == 0x3) goto L0344 L0296: $A = $high_args[1] L0297: if ($A == 0x0) goto L0301 L0298: if ($A != 0xffffffff) goto L0341 L0299: $A = $low_args[1] L0300: if !($A & 0x80000000) goto L0341 L0301: $A = $low_args[1] L0302: $A &= 0xfffffe7f L0303: if ($A == 0x4) goto L0344 L0304: $A = $high_args[1] L0305: if ($A == 0x0) goto L0309 L0306: if ($A != 0xffffffff) goto L0341 L0307: $A = $low_args[1] L0308: if !($A & 0x80000000) goto L0341 L0309: $A = $low_args[1] L0310: $A &= 0xfffffe7f L0311: if ($A == 0x5) goto L0344 L0312: $A = $high_args[1] L0313: if ($A == 0x0) goto L0317 L0314: if ($A != 0xffffffff) goto L0341 L0315: $A = $low_args[1] L0316: if !($A & 0x80000000) goto L0341 L0317: $A = $low_args[1] L0318: $A &= 0xfffffe7f L0319: if ($A == 0x9) goto L0344 L0320: $A = $high_args[1] L0321: if ($A == 0x0) goto L0325 L0322: if ($A != 0xffffffff) goto L0341 L0323: $A = $low_args[1] L0324: if !($A & 0x80000000) goto L0341 L0325: $A = $low_args[1] L0326: $A &= 0xfffffe7f L0327: if ($A == 0xa) goto L0344 L0328: return ERRNO(22) L0329: $A = $high_args[0] L0330: if ($A == 0x0) goto L0334 L0331: if ($A != 0xffffffff) goto L0341 L0332: $A = $low_args[0] L0333: if !($A & 0x80000000) goto L0341 L0334: $A = $low_args[0] L0335: if ($A == 0x10) goto L0344 L0336: $A = $high_args[0] L0337: if ($A == 0x0) goto L0342 L0338: if ($A != 0xffffffff) goto L0341 L0339: $A = $low_args[0] L0340: if ($A & 0x80000000) goto L0342 L0341: goto L0599 L0342: $A = $low_args[0] L0343: if ($A != 0xf) goto L0345 L0344: goto L0603 L0345: $A = $high_args[0] L0346: if ($A == 0x0) goto L0350 L0347: if ($A != 0xffffffff) goto L0599 L0348: $A = $low_args[0] L0349: if !($A & 0x80000000) goto L0599 L0350: $A = $low_args[0] L0351: if ($A == 0x3) goto L0603 L0352: $A = $high_args[0] L0353: if ($A == 0x0) goto L0357 L0354: if ($A != 0xffffffff) goto L0599 L0355: $A = $low_args[0] L0356: if !($A & 0x80000000) goto L0599 L0357: $A = $low_args[0] L0358: if ($A == 0x4) goto L0603 L0359: $A = $high_args[0] L0360: if ($A == 0x0) goto L0364 L0361: if ($A != 0xffffffff) goto L0599 L0362: $A = $low_args[0] L0363: if !($A & 0x80000000) goto L0599 L0364: $A = $low_args[0] L0365: if ($A == 0x53564d41) goto L0373 L0366: $A = $high_args[0] L0367: if ($A == 0x0) goto L0371 L0368: if ($A != 0xffffffff) goto L0599 L0369: $A = $low_args[0] L0370: if !($A & 0x80000000) goto L0599 L0371: $A = $low_args[0] L0372: if ($A == 0x59616d61) goto L0602, else goto L0380 L0373: $A = $high_args[1] L0374: if ($A == 0x0) goto L0378 L0375: if ($A != 0xffffffff) goto L0599 L0376: $A = $low_args[1] L0377: if !($A & 0x80000000) goto L0599 L0378: $A = $low_args[1] L0379: if ($A == 0x0) goto L0603 L0380: return TRAP(7) L0381: $A = $high_args[0] L0382: if ($A == 0x0) goto L0386 L0383: if ($A != 0xffffffff) goto L0599 L0384: $A = $low_args[0] L0385: if !($A & 0x80000000) goto L0599 L0386: $A = $low_args[0] L0387: if ($A == 0x0) goto L0603 L0388: $A = $high_args[0] L0389: if ($A == 0x0) goto L0393 L0390: if ($A != 0xffffffff) goto L0599 L0391: $A = $low_args[0] L0392: if !($A & 0x80000000) goto L0599 L0393: $A = $low_args[0] L0394: if ($A == 0x1) goto L0603 L0395: return TRAP(6) L0396: $A = $high_args[0] L0397: if ($A == 0x0) goto L0401 L0398: if ($A != 0xffffffff) goto L0599 L0399: $A = $low_args[0] L0400: if !($A & 0x80000000) goto L0599 L0401: $A = $low_args[0] L0402: if ($A != 0x0) goto L0604 L0403: $A = $high_args[1] L0404: if ($A == 0x0) goto L0408 L0405: if ($A != 0xffffffff) goto L0599 L0406: $A = $low_args[1] L0407: if !($A & 0x80000000) goto L0599 L0408: $A = $low_args[1] L0409: if ($A == 0x0) goto L0603 L0410: $A = $high_args[1] L0411: if ($A == 0x0) goto L0415 L0412: if ($A != 0xffffffff) goto L0599 L0413: $A = $low_args[1] L0414: if !($A & 0x80000000) goto L0599 L0415: $A = $low_args[1] L0416: if ($A == 0x1) goto L0603, else goto L0602 L0417: $A = $high_args[1] L0418: if ($A == 0x0) goto L0422 L0419: if ($A != 0xffffffff) goto L0599 L0420: $A = $low_args[1] L0421: if !($A & 0x80000000) goto L0599 L0422: $A = $low_args[1] L0423: if ($A == 0x3) goto L0603 L0424: $A = $high_args[1] L0425: if ($A == 0x0) goto L0429 L0426: if ($A != 0xffffffff) goto L0599 L0427: $A = $low_args[1] L0428: if !($A & 0x80000000) goto L0599 L0429: $A = $low_args[1] L0430: if ($A == 0x1) goto L0603 L0431: $A = $high_args[1] L0432: if ($A == 0x0) goto L0436 L0433: if ($A != 0xffffffff) goto L0599 L0434: $A = $low_args[1] L0435: if !($A & 0x80000000) goto L0599 L0436: $A = $low_args[1] L0437: if ($A == 0x40a) goto L0603 L0438: $A = $high_args[1] L0439: if ($A == 0x0) goto L0443 L0440: if ($A != 0xffffffff) goto L0599 L0441: $A = $low_args[1] L0442: if !($A & 0x80000000) goto L0599 L0443: $A = $low_args[1] L0444: if ($A == 0x2) goto L0603 L0445: $A = $high_args[1] L0446: if ($A == 0x0) goto L0450 L0447: if ($A != 0xffffffff) goto L0599 L0448: $A = $low_args[1] L0449: if !($A & 0x80000000) goto L0599 L0450: $A = $low_args[1] L0451: if ($A == 0x6) goto L0603 L0452: $A = $high_args[1] L0453: if ($A == 0x0) goto L0457 L0454: if ($A != 0xffffffff) goto L0599 L0455: $A = $low_args[1] L0456: if !($A & 0x80000000) goto L0599 L0457: $A = $low_args[1] L0458: if ($A == 0x7) goto L0603 L0459: $A = $high_args[1] L0460: if ($A == 0x0) goto L0464 L0461: if ($A != 0xffffffff) goto L0599 L0462: $A = $low_args[1] L0463: if !($A & 0x80000000) goto L0599 L0464: $A = $low_args[1] L0465: if ($A == 0x5) goto L0603 L0466: $A = $high_args[1] L0467: if ($A == 0x0) goto L0471 L0468: if ($A != 0xffffffff) goto L0599 L0469: $A = $low_args[1] L0470: if !($A & 0x80000000) goto L0599 L0471: $A = $low_args[1] L0472: if ($A == 0x0) goto L0603 L0473: $A = $high_args[1] L0474: if ($A == 0x0) goto L0478 L0475: if ($A != 0xffffffff) goto L0599 L0476: $A = $low_args[1] L0477: if !($A & 0x80000000) goto L0599 L0478: $A = $low_args[1] L0479: if ($A == 0x406) goto L0603 L0480: $A = $high_args[1] L0481: if ($A == 0x0) goto L0485 L0482: if ($A != 0xffffffff) goto L0599 L0483: $A = $low_args[1] L0484: if !($A & 0x80000000) goto L0599 L0485: $A = $low_args[1] L0486: if ($A == 0x4) goto L0496 L0487: $A = $high_args[1] L0488: if ($A == 0x0) goto L0492 L0489: if ($A != 0xffffffff) goto L0599 L0490: $A = $low_args[1] L0491: if !($A & 0x80000000) goto L0599 L0492: $A = $low_args[1] L0493: if ($A != 0x409) goto L0604 L0494: $A = $high_args[2] L0495: if ($A == 0x0) goto L0592, else goto L0604 L0496: $A = $high_args[2] L0497: if ($A != 0x0) goto L0604 L0498: $A = $low_args[2] L0499: if ($A & 0xffe363fc) goto L0604, else goto L0603 L0500: $A = $high_args[0] L0501: if ($A == 0x0) goto L0505 L0502: if ($A != 0xffffffff) goto L0599 L0503: $A = $low_args[0] L0504: if !($A & 0x80000000) goto L0599 L0505: $A = $low_args[0] L0506: if ($A == 0x1) goto L0603 L0507: return TRAP(5) L0508: $A = $high_args[0] L0509: if ($A != 0x0) goto L0512 L0510: $A = $low_args[0] L0511: if ($A == 0x3d0f00) goto L0603 L0512: $A = $low_args[0] L0513: if !($A & 0x10100) goto L0602 L0514: $A = $low_args[0] L0515: $A &= 0x4100 L0516: if ($A == 0x4100) goto L0602 L0517: return TRAP(4) L0518: $A = $high_args[1] L0519: if ($A == 0x0) goto L0523 L0520: if ($A != 0xffffffff) goto L0599 L0521: $A = $low_args[1] L0522: if !($A & 0x80000000) goto L0599 L0523: $A = $low_args[1] L0524: if ($A != 0x1) goto L0604 L0525: $A = $high_args[2] L0526: if ($A == 0x0) goto L0530 L0527: if ($A != 0xffffffff) goto L0599 L0528: $A = $low_args[2] L0529: if !($A & 0x80000000) goto L0599 L0530: $A = $low_args[2] L0531: if ($A == 0x2a) goto L0603, else goto L0604 L0532: $A = $high_args[0] L0533: if ($A == 0x0) goto L0537 L0534: if ($A != 0xffffffff) goto L0599 L0535: $A = $low_args[0] L0536: if !($A & 0x80000000) goto L0599 L0537: $A = $low_args[0] L0538: if ($A == 0x1) goto L0603, else goto L0604 L0539: $A = $high_args[2] L0540: if ($A == 0x0) goto L0544 L0541: if ($A != 0xffffffff) goto L0599 L0542: $A = $low_args[2] L0543: if !($A & 0x80000000) goto L0599 L0544: $A = $low_args[2] L0545: if ($A == 0x4) goto L0603 L0546: $A = $high_args[2] L0547: if ($A == 0x0) goto L0551 L0548: if ($A != 0xffffffff) goto L0599 L0549: $A = $low_args[2] L0550: if !($A & 0x80000000) goto L0599 L0551: $A = $low_args[2] L0552: if ($A == 0x1) goto L0603 L0553: $A = $high_args[2] L0554: if ($A == 0x0) goto L0558 L0555: if ($A != 0xffffffff) goto L0599 L0556: $A = $low_args[2] L0557: if !($A & 0x80000000) goto L0599 L0558: $A = $low_args[2] L0559: if ($A == 0x9) goto L0603 L0560: $A = $high_args[2] L0561: if ($A == 0x0) goto L0565 L0562: if ($A != 0xffffffff) goto L0599 L0563: $A = $low_args[2] L0564: if !($A & 0x80000000) goto L0599 L0565: $A = $low_args[2] L0566: if ($A == 0x0) goto L0603 L0567: $A = $high_args[2] L0568: if ($A == 0x0) goto L0572 L0569: if ($A != 0xffffffff) goto L0599 L0570: $A = $low_args[2] L0571: if !($A & 0x80000000) goto L0599 L0572: $A = $low_args[2] L0573: if ($A == 0x8) goto L0603, else goto L0602 L0574: $A = $high_args[1] L0575: if ($A != 0x0) goto L0578 L0576: $A = $low_args[1] L0577: if ($A == 0x5401) goto L0603 L0578: $A = $high_args[1] L0579: if ($A != 0x0) goto L0582 L0580: $A = $low_args[1] L0581: if ($A == 0x541b) goto L0603 L0582: $A = $high_args[1] L0583: if ($A != 0x0) goto L0586 L0584: $A = $low_args[1] L0585: if ($A == 0x40086200) goto L0603 L0586: return TRAP(3) L0587: $A = $high_args[2] L0588: if ($A == 0x0) goto L0592 L0589: if ($A != 0xffffffff) goto L0599 L0590: $A = $low_args[2] L0591: if !($A & 0x80000000) goto L0599 L0592: $A = $low_args[2] L0593: if ($A & 0xfffffff8) goto L0604, else goto L0603 L0594: $A = $high_args[3] L0595: if ($A == 0x0) goto L0600 L0596: if ($A != 0xffffffff) goto L0599 L0597: $A = $low_args[3] L0598: if ($A & 0x80000000) goto L0600 L0599: return TRAP(2) L0600: $A = $low_args[3] L0601: if ($A & 0xfffd97cc) goto L0604, else goto L0603 L0602: return ERRNO(1) #... 3 line(s) skipped ceccomp-4.0/test/emu_result/chromium.open000066400000000000000000000634661514205130000206450ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A == x86_64) goto L0004 L0003: return TRAP(10) L0004: $A = $syscall_nr L0005: if !($A & 0x40000000) goto L0007 L0006: return TRAP(9) L0007: if ($A <= sched_get_priority_max) goto L0082 L0008: if ($A <= pselect6) goto L0045 L0009: if ($A <= name_to_handle_at) goto L0028 L0010: if ($A <= io_pgetevents) goto L0020 L0011: if ($A <= close_range) goto L0017 L0012: if ($A <= process_madvise) goto L0016 L0013: if ($A <= mseal) goto L0015 L0014: if ($A <= setxattrat) goto L0081 L0015: goto L0604 L0016: if ($A >= faccessat2) goto L0171, else goto L0272 L0017: if ($A <= uretprobe) goto L0019 L0018: if ($A >= pidfd_open) goto L0172, else goto L0272 L0019: if ($A >= rseq) goto L0081, else goto L0272 L0020: if ($A <= kexec_file_load) goto L0025 L0021: if ($A <= pkey_alloc) goto L0024 L0022: if ($A <= pkey_free) goto L0173 L0023: if ($A >= statx) goto L0164, else goto L0081 L0024: if ($A >= pkey_mprotect) goto L0159, else goto L0272 L0025: if ($A <= getrandom) goto L0027 L0026: if ($A >= memfd_create) goto L0081, else goto L0180 L0027: if ($A >= renameat2) goto L0171, else goto L0272 L0028: if ($A <= timerfd_settime) goto L0037 L0029: if ($A <= pipe2) goto L0034 L0030: if ($A <= rt_tgsigqueueinfo) goto L0033 L0031: if ($A <= perf_event_open) goto L0194 L0032: if ($A >= prlimit64) goto L0187, else goto L0272 L0033: if ($A >= inotify_init1) goto L0272, else goto L0201 L0034: if ($A <= signalfd4) goto L0036 L0035: if ($A >= eventfd2) goto L0081, else goto L0272 L0036: if ($A >= accept4) goto L0171, else goto L0272 L0037: if ($A <= utimensat) goto L0042 L0038: if ($A <= signalfd) goto L0041 L0039: if ($A <= eventfd) goto L0272 L0040: if ($A >= fallocate) goto L0171, else goto L0081 L0041: if ($A >= epoll_pwait) goto L0081, else goto L0171 L0042: if ($A <= set_robust_list) goto L0044 L0043: if ($A >= get_robust_list) goto L0272, else goto L0081 L0044: if ($A >= unshare) goto L0272, else goto L0081 L0045: if ($A <= lookup_dcookie) goto L0063 L0046: if ($A <= exit_group) goto L0055 L0047: if ($A <= waitid) goto L0052 L0048: if ($A <= openat) goto L0051 L0049: if ($A <= newfstatat) goto L0171 L0050: if ($A >= unlinkat) goto L0171, else goto L0208 L0051: if ($A >= add_key) goto L0272, else goto L0081 L0052: if ($A <= utimes) goto L0054 L0053: if ($A >= vserver) goto L0272, else goto L0171 L0054: if ($A >= tgkill) goto L0130, else goto L0081 L0055: if ($A <= set_tid_address) goto L0060 L0056: if ($A <= semtimedop) goto L0059 L0057: if ($A <= fadvise64) goto L0171 L0058: if ($A >= clock_gettime) goto L0209, else goto L0272 L0059: if ($A >= restart_syscall) goto L0081, else goto L0272 L0060: if ($A <= epoll_ctl_old) goto L0062 L0061: if ($A >= getdents64) goto L0171, else goto L0272 L0062: if ($A >= epoll_create) goto L0081, else goto L0171 L0063: if ($A <= create_module) goto L0073 L0064: if ($A <= time) goto L0069 L0065: if ($A <= sched_setaffinity) goto L0068 L0066: if ($A <= sched_getaffinity) goto L0272 L0067: if ($A >= set_thread_area) goto L0272, else goto L0090 L0068: if ($A >= futex) goto L0273, else goto L0081 L0069: if ($A <= readahead) goto L0072 L0070: if ($A <= tkill) goto L0272 L0071: goto L0507 L0072: if ($A >= gettid) goto L0081, else goto L0272 L0073: if ($A <= prctl) goto L0078 L0074: if ($A <= setrlimit) goto L0077 L0075: if ($A <= chroot) goto L0081 L0076: if ($A >= iopl) goto L0171, else goto L0272 L0077: if ($A >= arch_prctl) goto L0272, else goto L0329 L0078: if ($A <= mlock) goto L0080 L0079: if ($A >= mlockall) goto L0272, else goto L0081 L0080: if ($A >= sched_rr_get_interval) goto L0272 L0081: goto L0603 L0082: if ($A <= truncate) goto L0120 L0083: if ($A <= getresuid) goto L0103 L0084: if ($A <= rt_sigqueueinfo) goto L0095 L0085: if ($A <= ustat) goto L0092 L0086: if ($A <= getpriority) goto L0091 L0087: if ($A >= sched_setparam) goto L0089 L0088: goto L0396 L0089: if ($A <= sched_getparam) goto L0272 L0090: goto L0381 L0091: if ($A >= fstatfs) goto L0272, else goto L0171 L0092: if ($A <= utime) goto L0094 L0093: if ($A >= personality) goto L0272, else goto L0171 L0094: if ($A >= sigaltstack) goto L0344, else goto L0272 L0095: if ($A <= setfsuid) goto L0100 L0096: if ($A <= capset) goto L0099 L0097: if ($A <= rt_sigpending) goto L0171 L0098: if ($A >= rt_sigtimedwait) goto L0344, else goto L0272 L0099: if ($A >= getsid) goto L0344, else goto L0171 L0100: if ($A <= getresgid) goto L0102 L0101: if ($A >= getpgid) goto L0272, else goto L0344 L0102: if ($A >= setresgid) goto L0171, else goto L0344 L0103: if ($A <= getgid) goto L0112 L0104: if ($A <= getppid) goto L0109 L0105: if ($A <= setreuid) goto L0108 L0106: if ($A <= getgroups) goto L0171 L0107: if ($A >= setgroups) goto L0171, else goto L0344 L0108: if ($A >= getpgrp) goto L0272, else goto L0344 L0109: if ($A <= geteuid) goto L0111 L0110: if ($A >= setpgid) goto L0272, else goto L0344 L0111: if ($A >= setuid) goto L0171, else goto L0344 L0112: if ($A <= getrusage) goto L0117 L0113: if ($A <= ptrace) goto L0116 L0114: if ($A <= getuid) goto L0272 L0115: if ($A >= syslog) goto L0272, else goto L0344 L0116: if ($A >= sysinfo) goto L0344, else goto L0272 L0117: if ($A <= getdents) goto L0119 L0118: if ($A >= gettimeofday) goto L0344, else goto L0171 L0119: if ($A >= ftruncate) goto L0344, else goto L0171 L0120: if ($A <= dup) goto L0143 L0121: if ($A <= clone) goto L0133 L0122: if ($A <= uname) goto L0128 L0123: if ($A <= fcntl) goto L0127 L0124: if ($A >= flock) goto L0126 L0125: goto L0417 L0126: if ($A >= fsync) goto L0344, else goto L0272 L0127: if ($A >= semget) goto L0171, else goto L0344 L0128: if ($A <= exit) goto L0131 L0129: if ($A <= kill) goto L0344 L0130: goto L0500 L0131: if ($A >= fork) goto L0171 L0132: goto L0508 L0133: if ($A <= sendto) goto L0140 L0134: if ($A <= getsockname) goto L0139 L0135: if ($A <= socketpair) goto L0272 L0136: if ($A >= setsockopt) goto L0138 L0137: goto L0532 L0138: goto L0518 L0139: if ($A >= bind) goto L0171, else goto L0344 L0140: if ($A <= getpid) goto L0142 L0141: if ($A >= sendfile) goto L0171, else goto L0344 L0142: if ($A >= getitimer) goto L0272, else goto L0344 L0143: if ($A <= munmap) goto L0154 L0144: if ($A <= pipe) goto L0150 L0145: if ($A <= mincore) goto L0149 L0146: if ($A <= madvise) goto L0344 L0147: if ($A >= shmget) goto L0171 L0148: goto L0539 L0149: if ($A >= msync) goto L0272, else goto L0344 L0150: if ($A <= pread64) goto L0152 L0151: if ($A >= access) goto L0171, else goto L0344 L0152: if ($A <= ioctl) goto L0344 L0153: goto L0574 L0154: if ($A <= fstat) goto L0161 L0155: if ($A <= poll) goto L0160 L0156: if ($A <= mmap) goto L0344 L0157: if ($A >= mprotect) goto L0159 L0158: goto L0594 L0159: goto L0587 L0160: if ($A >= lstat) goto L0171, else goto L0344 L0161: if ($A <= close) goto L0163 L0162: if ($A >= stat) goto L0171, else goto L0344 L0163: if ($A >= open) goto L0171, else goto L0344 L0164: $A = $high_args[3] L0165: if ($A == 0x0) goto L0169 L0166: if ($A != 0xffffffff) goto L0341 L0167: $A = $low_args[3] L0168: if !($A & 0x80000000) goto L0341 L0169: $A = $low_args[3] L0170: if ($A == 0x7ff) goto L0172 L0171: goto L0602 L0172: return ERRNO(38) L0173: $A = $high_args[0] L0174: if ($A == 0x0) goto L0178 L0175: if ($A != 0xffffffff) goto L0341 L0176: $A = $low_args[0] L0177: if !($A & 0x80000000) goto L0341 L0178: $A = $low_args[0] L0179: if ($A == 0x0) goto L0344, else goto L0272 L0180: $A = $high_args[2] L0181: if ($A == 0x0) goto L0185 L0182: if ($A != 0xffffffff) goto L0341 L0183: $A = $low_args[2] L0184: if !($A & 0x80000000) goto L0341 L0185: $A = $low_args[2] L0186: if ($A & 0xfffffffa) goto L0272, else goto L0344 L0187: $A = $high_args[0] L0188: if ($A == 0x0) goto L0192 L0189: if ($A != 0xffffffff) goto L0341 L0190: $A = $low_args[0] L0191: if !($A & 0x80000000) goto L0341 L0192: $A = $low_args[0] L0193: if ($A == 0x0) goto L0344 L0194: $A = $high_args[0] L0195: if ($A == 0x0) goto L0199 L0196: if ($A != 0xffffffff) goto L0341 L0197: $A = $low_args[0] L0198: if !($A & 0x80000000) goto L0341 L0199: $A = $low_args[0] L0200: goto L0416 L0201: $A = $high_args[1] L0202: if ($A == 0x0) goto L0206 L0203: if ($A != 0xffffffff) goto L0341 L0204: $A = $low_args[1] L0205: if !($A & 0x80000000) goto L0341 L0206: $A = $low_args[1] L0207: if ($A & 0xfff7b7ff) goto L0272, else goto L0344 L0208: return TRAP(8) L0209: $A = $high_args[0] L0210: if ($A == 0x0) goto L0214 L0211: if ($A != 0xffffffff) goto L0341 L0212: $A = $low_args[0] L0213: if !($A & 0x80000000) goto L0341 L0214: $A = $low_args[0] L0215: if ($A & 0x80000000) goto L0272 L0216: $A = $high_args[0] L0217: if ($A == 0x0) goto L0221 L0218: if ($A != 0xffffffff) goto L0341 L0219: $A = $low_args[0] L0220: if !($A & 0x80000000) goto L0341 L0221: $A = $low_args[0] L0222: if ($A == 0x7) goto L0344 L0223: $A = $high_args[0] L0224: if ($A == 0x0) goto L0228 L0225: if ($A != 0xffffffff) goto L0341 L0226: $A = $low_args[0] L0227: if !($A & 0x80000000) goto L0341 L0228: $A = $low_args[0] L0229: if ($A == 0x1) goto L0344 L0230: $A = $high_args[0] L0231: if ($A == 0x0) goto L0235 L0232: if ($A != 0xffffffff) goto L0341 L0233: $A = $low_args[0] L0234: if !($A & 0x80000000) goto L0341 L0235: $A = $low_args[0] L0236: if ($A == 0x6) goto L0344 L0237: $A = $high_args[0] L0238: if ($A == 0x0) goto L0242 L0239: if ($A != 0xffffffff) goto L0341 L0240: $A = $low_args[0] L0241: if !($A & 0x80000000) goto L0341 L0242: $A = $low_args[0] L0243: if ($A == 0x4) goto L0344 L0244: $A = $high_args[0] L0245: if ($A == 0x0) goto L0249 L0246: if ($A != 0xffffffff) goto L0341 L0247: $A = $low_args[0] L0248: if !($A & 0x80000000) goto L0341 L0249: $A = $low_args[0] L0250: if ($A == 0x2) goto L0344 L0251: $A = $high_args[0] L0252: if ($A == 0x0) goto L0256 L0253: if ($A != 0xffffffff) goto L0341 L0254: $A = $low_args[0] L0255: if !($A & 0x80000000) goto L0341 L0256: $A = $low_args[0] L0257: if ($A == 0x0) goto L0344 L0258: $A = $high_args[0] L0259: if ($A == 0x0) goto L0263 L0260: if ($A != 0xffffffff) goto L0341 L0261: $A = $low_args[0] L0262: if !($A & 0x80000000) goto L0341 L0263: $A = $low_args[0] L0264: if ($A == 0x5) goto L0344 L0265: $A = $high_args[0] L0266: if ($A == 0x0) goto L0270 L0267: if ($A != 0xffffffff) goto L0341 L0268: $A = $low_args[0] L0269: if !($A & 0x80000000) goto L0341 L0270: $A = $low_args[0] L0271: if ($A == 0x3) goto L0344 L0272: goto L0604 L0273: $A = $high_args[1] L0274: if ($A == 0x0) goto L0278 L0275: if ($A != 0xffffffff) goto L0341 L0276: $A = $low_args[1] L0277: if !($A & 0x80000000) goto L0341 L0278: $A = $low_args[1] L0279: if !($A & 0xfffffe7f) goto L0344 L0280: $A = $high_args[1] L0281: if ($A == 0x0) goto L0285 L0282: if ($A != 0xffffffff) goto L0341 L0283: $A = $low_args[1] L0284: if !($A & 0x80000000) goto L0341 L0285: $A = $low_args[1] L0286: $A &= 0xfffffe7f L0287: if ($A == 0x1) goto L0344 L0288: $A = $high_args[1] L0289: if ($A == 0x0) goto L0293 L0290: if ($A != 0xffffffff) goto L0341 L0291: $A = $low_args[1] L0292: if !($A & 0x80000000) goto L0341 L0293: $A = $low_args[1] L0294: $A &= 0xfffffe7f L0295: if ($A == 0x3) goto L0344 L0296: $A = $high_args[1] L0297: if ($A == 0x0) goto L0301 L0298: if ($A != 0xffffffff) goto L0341 L0299: $A = $low_args[1] L0300: if !($A & 0x80000000) goto L0341 L0301: $A = $low_args[1] L0302: $A &= 0xfffffe7f L0303: if ($A == 0x4) goto L0344 L0304: $A = $high_args[1] L0305: if ($A == 0x0) goto L0309 L0306: if ($A != 0xffffffff) goto L0341 L0307: $A = $low_args[1] L0308: if !($A & 0x80000000) goto L0341 L0309: $A = $low_args[1] L0310: $A &= 0xfffffe7f L0311: if ($A == 0x5) goto L0344 L0312: $A = $high_args[1] L0313: if ($A == 0x0) goto L0317 L0314: if ($A != 0xffffffff) goto L0341 L0315: $A = $low_args[1] L0316: if !($A & 0x80000000) goto L0341 L0317: $A = $low_args[1] L0318: $A &= 0xfffffe7f L0319: if ($A == 0x9) goto L0344 L0320: $A = $high_args[1] L0321: if ($A == 0x0) goto L0325 L0322: if ($A != 0xffffffff) goto L0341 L0323: $A = $low_args[1] L0324: if !($A & 0x80000000) goto L0341 L0325: $A = $low_args[1] L0326: $A &= 0xfffffe7f L0327: if ($A == 0xa) goto L0344 L0328: return ERRNO(22) L0329: $A = $high_args[0] L0330: if ($A == 0x0) goto L0334 L0331: if ($A != 0xffffffff) goto L0341 L0332: $A = $low_args[0] L0333: if !($A & 0x80000000) goto L0341 L0334: $A = $low_args[0] L0335: if ($A == 0x10) goto L0344 L0336: $A = $high_args[0] L0337: if ($A == 0x0) goto L0342 L0338: if ($A != 0xffffffff) goto L0341 L0339: $A = $low_args[0] L0340: if ($A & 0x80000000) goto L0342 L0341: goto L0599 L0342: $A = $low_args[0] L0343: if ($A != 0xf) goto L0345 L0344: goto L0603 L0345: $A = $high_args[0] L0346: if ($A == 0x0) goto L0350 L0347: if ($A != 0xffffffff) goto L0599 L0348: $A = $low_args[0] L0349: if !($A & 0x80000000) goto L0599 L0350: $A = $low_args[0] L0351: if ($A == 0x3) goto L0603 L0352: $A = $high_args[0] L0353: if ($A == 0x0) goto L0357 L0354: if ($A != 0xffffffff) goto L0599 L0355: $A = $low_args[0] L0356: if !($A & 0x80000000) goto L0599 L0357: $A = $low_args[0] L0358: if ($A == 0x4) goto L0603 L0359: $A = $high_args[0] L0360: if ($A == 0x0) goto L0364 L0361: if ($A != 0xffffffff) goto L0599 L0362: $A = $low_args[0] L0363: if !($A & 0x80000000) goto L0599 L0364: $A = $low_args[0] L0365: if ($A == 0x53564d41) goto L0373 L0366: $A = $high_args[0] L0367: if ($A == 0x0) goto L0371 L0368: if ($A != 0xffffffff) goto L0599 L0369: $A = $low_args[0] L0370: if !($A & 0x80000000) goto L0599 L0371: $A = $low_args[0] L0372: if ($A == 0x59616d61) goto L0602, else goto L0380 L0373: $A = $high_args[1] L0374: if ($A == 0x0) goto L0378 L0375: if ($A != 0xffffffff) goto L0599 L0376: $A = $low_args[1] L0377: if !($A & 0x80000000) goto L0599 L0378: $A = $low_args[1] L0379: if ($A == 0x0) goto L0603 L0380: return TRAP(7) L0381: $A = $high_args[0] L0382: if ($A == 0x0) goto L0386 L0383: if ($A != 0xffffffff) goto L0599 L0384: $A = $low_args[0] L0385: if !($A & 0x80000000) goto L0599 L0386: $A = $low_args[0] L0387: if ($A == 0x0) goto L0603 L0388: $A = $high_args[0] L0389: if ($A == 0x0) goto L0393 L0390: if ($A != 0xffffffff) goto L0599 L0391: $A = $low_args[0] L0392: if !($A & 0x80000000) goto L0599 L0393: $A = $low_args[0] L0394: if ($A == 0x1) goto L0603 L0395: return TRAP(6) L0396: $A = $high_args[0] L0397: if ($A == 0x0) goto L0401 L0398: if ($A != 0xffffffff) goto L0599 L0399: $A = $low_args[0] L0400: if !($A & 0x80000000) goto L0599 L0401: $A = $low_args[0] L0402: if ($A != 0x0) goto L0604 L0403: $A = $high_args[1] L0404: if ($A == 0x0) goto L0408 L0405: if ($A != 0xffffffff) goto L0599 L0406: $A = $low_args[1] L0407: if !($A & 0x80000000) goto L0599 L0408: $A = $low_args[1] L0409: if ($A == 0x0) goto L0603 L0410: $A = $high_args[1] L0411: if ($A == 0x0) goto L0415 L0412: if ($A != 0xffffffff) goto L0599 L0413: $A = $low_args[1] L0414: if !($A & 0x80000000) goto L0599 L0415: $A = $low_args[1] L0416: if ($A == 0x1) goto L0603, else goto L0602 L0417: $A = $high_args[1] L0418: if ($A == 0x0) goto L0422 L0419: if ($A != 0xffffffff) goto L0599 L0420: $A = $low_args[1] L0421: if !($A & 0x80000000) goto L0599 L0422: $A = $low_args[1] L0423: if ($A == 0x3) goto L0603 L0424: $A = $high_args[1] L0425: if ($A == 0x0) goto L0429 L0426: if ($A != 0xffffffff) goto L0599 L0427: $A = $low_args[1] L0428: if !($A & 0x80000000) goto L0599 L0429: $A = $low_args[1] L0430: if ($A == 0x1) goto L0603 L0431: $A = $high_args[1] L0432: if ($A == 0x0) goto L0436 L0433: if ($A != 0xffffffff) goto L0599 L0434: $A = $low_args[1] L0435: if !($A & 0x80000000) goto L0599 L0436: $A = $low_args[1] L0437: if ($A == 0x40a) goto L0603 L0438: $A = $high_args[1] L0439: if ($A == 0x0) goto L0443 L0440: if ($A != 0xffffffff) goto L0599 L0441: $A = $low_args[1] L0442: if !($A & 0x80000000) goto L0599 L0443: $A = $low_args[1] L0444: if ($A == 0x2) goto L0603 L0445: $A = $high_args[1] L0446: if ($A == 0x0) goto L0450 L0447: if ($A != 0xffffffff) goto L0599 L0448: $A = $low_args[1] L0449: if !($A & 0x80000000) goto L0599 L0450: $A = $low_args[1] L0451: if ($A == 0x6) goto L0603 L0452: $A = $high_args[1] L0453: if ($A == 0x0) goto L0457 L0454: if ($A != 0xffffffff) goto L0599 L0455: $A = $low_args[1] L0456: if !($A & 0x80000000) goto L0599 L0457: $A = $low_args[1] L0458: if ($A == 0x7) goto L0603 L0459: $A = $high_args[1] L0460: if ($A == 0x0) goto L0464 L0461: if ($A != 0xffffffff) goto L0599 L0462: $A = $low_args[1] L0463: if !($A & 0x80000000) goto L0599 L0464: $A = $low_args[1] L0465: if ($A == 0x5) goto L0603 L0466: $A = $high_args[1] L0467: if ($A == 0x0) goto L0471 L0468: if ($A != 0xffffffff) goto L0599 L0469: $A = $low_args[1] L0470: if !($A & 0x80000000) goto L0599 L0471: $A = $low_args[1] L0472: if ($A == 0x0) goto L0603 L0473: $A = $high_args[1] L0474: if ($A == 0x0) goto L0478 L0475: if ($A != 0xffffffff) goto L0599 L0476: $A = $low_args[1] L0477: if !($A & 0x80000000) goto L0599 L0478: $A = $low_args[1] L0479: if ($A == 0x406) goto L0603 L0480: $A = $high_args[1] L0481: if ($A == 0x0) goto L0485 L0482: if ($A != 0xffffffff) goto L0599 L0483: $A = $low_args[1] L0484: if !($A & 0x80000000) goto L0599 L0485: $A = $low_args[1] L0486: if ($A == 0x4) goto L0496 L0487: $A = $high_args[1] L0488: if ($A == 0x0) goto L0492 L0489: if ($A != 0xffffffff) goto L0599 L0490: $A = $low_args[1] L0491: if !($A & 0x80000000) goto L0599 L0492: $A = $low_args[1] L0493: if ($A != 0x409) goto L0604 L0494: $A = $high_args[2] L0495: if ($A == 0x0) goto L0592, else goto L0604 L0496: $A = $high_args[2] L0497: if ($A != 0x0) goto L0604 L0498: $A = $low_args[2] L0499: if ($A & 0xffe363fc) goto L0604, else goto L0603 L0500: $A = $high_args[0] L0501: if ($A == 0x0) goto L0505 L0502: if ($A != 0xffffffff) goto L0599 L0503: $A = $low_args[0] L0504: if !($A & 0x80000000) goto L0599 L0505: $A = $low_args[0] L0506: if ($A == 0x1) goto L0603 L0507: return TRAP(5) L0508: $A = $high_args[0] L0509: if ($A != 0x0) goto L0512 L0510: $A = $low_args[0] L0511: if ($A == 0x3d0f00) goto L0603 L0512: $A = $low_args[0] L0513: if !($A & 0x10100) goto L0602 L0514: $A = $low_args[0] L0515: $A &= 0x4100 L0516: if ($A == 0x4100) goto L0602 L0517: return TRAP(4) L0518: $A = $high_args[1] L0519: if ($A == 0x0) goto L0523 L0520: if ($A != 0xffffffff) goto L0599 L0521: $A = $low_args[1] L0522: if !($A & 0x80000000) goto L0599 L0523: $A = $low_args[1] L0524: if ($A != 0x1) goto L0604 L0525: $A = $high_args[2] L0526: if ($A == 0x0) goto L0530 L0527: if ($A != 0xffffffff) goto L0599 L0528: $A = $low_args[2] L0529: if !($A & 0x80000000) goto L0599 L0530: $A = $low_args[2] L0531: if ($A == 0x2a) goto L0603, else goto L0604 L0532: $A = $high_args[0] L0533: if ($A == 0x0) goto L0537 L0534: if ($A != 0xffffffff) goto L0599 L0535: $A = $low_args[0] L0536: if !($A & 0x80000000) goto L0599 L0537: $A = $low_args[0] L0538: if ($A == 0x1) goto L0603, else goto L0604 L0539: $A = $high_args[2] L0540: if ($A == 0x0) goto L0544 L0541: if ($A != 0xffffffff) goto L0599 L0542: $A = $low_args[2] L0543: if !($A & 0x80000000) goto L0599 L0544: $A = $low_args[2] L0545: if ($A == 0x4) goto L0603 L0546: $A = $high_args[2] L0547: if ($A == 0x0) goto L0551 L0548: if ($A != 0xffffffff) goto L0599 L0549: $A = $low_args[2] L0550: if !($A & 0x80000000) goto L0599 L0551: $A = $low_args[2] L0552: if ($A == 0x1) goto L0603 L0553: $A = $high_args[2] L0554: if ($A == 0x0) goto L0558 L0555: if ($A != 0xffffffff) goto L0599 L0556: $A = $low_args[2] L0557: if !($A & 0x80000000) goto L0599 L0558: $A = $low_args[2] L0559: if ($A == 0x9) goto L0603 L0560: $A = $high_args[2] L0561: if ($A == 0x0) goto L0565 L0562: if ($A != 0xffffffff) goto L0599 L0563: $A = $low_args[2] L0564: if !($A & 0x80000000) goto L0599 L0565: $A = $low_args[2] L0566: if ($A == 0x0) goto L0603 L0567: $A = $high_args[2] L0568: if ($A == 0x0) goto L0572 L0569: if ($A != 0xffffffff) goto L0599 L0570: $A = $low_args[2] L0571: if !($A & 0x80000000) goto L0599 L0572: $A = $low_args[2] L0573: if ($A == 0x8) goto L0603, else goto L0602 L0574: $A = $high_args[1] L0575: if ($A != 0x0) goto L0578 L0576: $A = $low_args[1] L0577: if ($A == 0x5401) goto L0603 L0578: $A = $high_args[1] L0579: if ($A != 0x0) goto L0582 L0580: $A = $low_args[1] L0581: if ($A == 0x541b) goto L0603 L0582: $A = $high_args[1] L0583: if ($A != 0x0) goto L0586 L0584: $A = $low_args[1] L0585: if ($A == 0x40086200) goto L0603 L0586: return TRAP(3) L0587: $A = $high_args[2] L0588: if ($A == 0x0) goto L0592 L0589: if ($A != 0xffffffff) goto L0599 L0590: $A = $low_args[2] L0591: if !($A & 0x80000000) goto L0599 L0592: $A = $low_args[2] L0593: if ($A & 0xfffffff8) goto L0604, else goto L0603 L0594: $A = $high_args[3] L0595: if ($A == 0x0) goto L0600 L0596: if ($A != 0xffffffff) goto L0599 L0597: $A = $low_args[3] L0598: if ($A & 0x80000000) goto L0600 L0599: return TRAP(2) L0600: $A = $low_args[3] L0601: if ($A & 0xfffd97cc) goto L0604, else goto L0603 L0602: return ERRNO(1) #... 3 line(s) skipped ceccomp-4.0/test/emu_result/chromium.pipe000066400000000000000000000634661514205130000206410ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A == x86_64) goto L0004 L0003: return TRAP(10) L0004: $A = $syscall_nr L0005: if !($A & 0x40000000) goto L0007 L0006: return TRAP(9) L0007: if ($A <= sched_get_priority_max) goto L0082 L0008: if ($A <= pselect6) goto L0045 L0009: if ($A <= name_to_handle_at) goto L0028 L0010: if ($A <= io_pgetevents) goto L0020 L0011: if ($A <= close_range) goto L0017 L0012: if ($A <= process_madvise) goto L0016 L0013: if ($A <= mseal) goto L0015 L0014: if ($A <= setxattrat) goto L0081 L0015: goto L0604 L0016: if ($A >= faccessat2) goto L0171, else goto L0272 L0017: if ($A <= uretprobe) goto L0019 L0018: if ($A >= pidfd_open) goto L0172, else goto L0272 L0019: if ($A >= rseq) goto L0081, else goto L0272 L0020: if ($A <= kexec_file_load) goto L0025 L0021: if ($A <= pkey_alloc) goto L0024 L0022: if ($A <= pkey_free) goto L0173 L0023: if ($A >= statx) goto L0164, else goto L0081 L0024: if ($A >= pkey_mprotect) goto L0159, else goto L0272 L0025: if ($A <= getrandom) goto L0027 L0026: if ($A >= memfd_create) goto L0081, else goto L0180 L0027: if ($A >= renameat2) goto L0171, else goto L0272 L0028: if ($A <= timerfd_settime) goto L0037 L0029: if ($A <= pipe2) goto L0034 L0030: if ($A <= rt_tgsigqueueinfo) goto L0033 L0031: if ($A <= perf_event_open) goto L0194 L0032: if ($A >= prlimit64) goto L0187, else goto L0272 L0033: if ($A >= inotify_init1) goto L0272, else goto L0201 L0034: if ($A <= signalfd4) goto L0036 L0035: if ($A >= eventfd2) goto L0081, else goto L0272 L0036: if ($A >= accept4) goto L0171, else goto L0272 L0037: if ($A <= utimensat) goto L0042 L0038: if ($A <= signalfd) goto L0041 L0039: if ($A <= eventfd) goto L0272 L0040: if ($A >= fallocate) goto L0171, else goto L0081 L0041: if ($A >= epoll_pwait) goto L0081, else goto L0171 L0042: if ($A <= set_robust_list) goto L0044 L0043: if ($A >= get_robust_list) goto L0272, else goto L0081 L0044: if ($A >= unshare) goto L0272, else goto L0081 L0045: if ($A <= lookup_dcookie) goto L0063 L0046: if ($A <= exit_group) goto L0055 L0047: if ($A <= waitid) goto L0052 L0048: if ($A <= openat) goto L0051 L0049: if ($A <= newfstatat) goto L0171 L0050: if ($A >= unlinkat) goto L0171, else goto L0208 L0051: if ($A >= add_key) goto L0272, else goto L0081 L0052: if ($A <= utimes) goto L0054 L0053: if ($A >= vserver) goto L0272, else goto L0171 L0054: if ($A >= tgkill) goto L0130, else goto L0081 L0055: if ($A <= set_tid_address) goto L0060 L0056: if ($A <= semtimedop) goto L0059 L0057: if ($A <= fadvise64) goto L0171 L0058: if ($A >= clock_gettime) goto L0209, else goto L0272 L0059: if ($A >= restart_syscall) goto L0081, else goto L0272 L0060: if ($A <= epoll_ctl_old) goto L0062 L0061: if ($A >= getdents64) goto L0171, else goto L0272 L0062: if ($A >= epoll_create) goto L0081, else goto L0171 L0063: if ($A <= create_module) goto L0073 L0064: if ($A <= time) goto L0069 L0065: if ($A <= sched_setaffinity) goto L0068 L0066: if ($A <= sched_getaffinity) goto L0272 L0067: if ($A >= set_thread_area) goto L0272, else goto L0090 L0068: if ($A >= futex) goto L0273, else goto L0081 L0069: if ($A <= readahead) goto L0072 L0070: if ($A <= tkill) goto L0272 L0071: goto L0507 L0072: if ($A >= gettid) goto L0081, else goto L0272 L0073: if ($A <= prctl) goto L0078 L0074: if ($A <= setrlimit) goto L0077 L0075: if ($A <= chroot) goto L0081 L0076: if ($A >= iopl) goto L0171, else goto L0272 L0077: if ($A >= arch_prctl) goto L0272, else goto L0329 L0078: if ($A <= mlock) goto L0080 L0079: if ($A >= mlockall) goto L0272, else goto L0081 L0080: if ($A >= sched_rr_get_interval) goto L0272 L0081: goto L0603 L0082: if ($A <= truncate) goto L0120 L0083: if ($A <= getresuid) goto L0103 L0084: if ($A <= rt_sigqueueinfo) goto L0095 L0085: if ($A <= ustat) goto L0092 L0086: if ($A <= getpriority) goto L0091 L0087: if ($A >= sched_setparam) goto L0089 L0088: goto L0396 L0089: if ($A <= sched_getparam) goto L0272 L0090: goto L0381 L0091: if ($A >= fstatfs) goto L0272, else goto L0171 L0092: if ($A <= utime) goto L0094 L0093: if ($A >= personality) goto L0272, else goto L0171 L0094: if ($A >= sigaltstack) goto L0344, else goto L0272 L0095: if ($A <= setfsuid) goto L0100 L0096: if ($A <= capset) goto L0099 L0097: if ($A <= rt_sigpending) goto L0171 L0098: if ($A >= rt_sigtimedwait) goto L0344, else goto L0272 L0099: if ($A >= getsid) goto L0344, else goto L0171 L0100: if ($A <= getresgid) goto L0102 L0101: if ($A >= getpgid) goto L0272, else goto L0344 L0102: if ($A >= setresgid) goto L0171, else goto L0344 L0103: if ($A <= getgid) goto L0112 L0104: if ($A <= getppid) goto L0109 L0105: if ($A <= setreuid) goto L0108 L0106: if ($A <= getgroups) goto L0171 L0107: if ($A >= setgroups) goto L0171, else goto L0344 L0108: if ($A >= getpgrp) goto L0272, else goto L0344 L0109: if ($A <= geteuid) goto L0111 L0110: if ($A >= setpgid) goto L0272, else goto L0344 L0111: if ($A >= setuid) goto L0171, else goto L0344 L0112: if ($A <= getrusage) goto L0117 L0113: if ($A <= ptrace) goto L0116 L0114: if ($A <= getuid) goto L0272 L0115: if ($A >= syslog) goto L0272, else goto L0344 L0116: if ($A >= sysinfo) goto L0344, else goto L0272 L0117: if ($A <= getdents) goto L0119 L0118: if ($A >= gettimeofday) goto L0344, else goto L0171 L0119: if ($A >= ftruncate) goto L0344, else goto L0171 L0120: if ($A <= dup) goto L0143 L0121: if ($A <= clone) goto L0133 L0122: if ($A <= uname) goto L0128 L0123: if ($A <= fcntl) goto L0127 L0124: if ($A >= flock) goto L0126 L0125: goto L0417 L0126: if ($A >= fsync) goto L0344, else goto L0272 L0127: if ($A >= semget) goto L0171, else goto L0344 L0128: if ($A <= exit) goto L0131 L0129: if ($A <= kill) goto L0344 L0130: goto L0500 L0131: if ($A >= fork) goto L0171 L0132: goto L0508 L0133: if ($A <= sendto) goto L0140 L0134: if ($A <= getsockname) goto L0139 L0135: if ($A <= socketpair) goto L0272 L0136: if ($A >= setsockopt) goto L0138 L0137: goto L0532 L0138: goto L0518 L0139: if ($A >= bind) goto L0171, else goto L0344 L0140: if ($A <= getpid) goto L0142 L0141: if ($A >= sendfile) goto L0171, else goto L0344 L0142: if ($A >= getitimer) goto L0272, else goto L0344 L0143: if ($A <= munmap) goto L0154 L0144: if ($A <= pipe) goto L0150 L0145: if ($A <= mincore) goto L0149 L0146: if ($A <= madvise) goto L0344 L0147: if ($A >= shmget) goto L0171 L0148: goto L0539 L0149: if ($A >= msync) goto L0272, else goto L0344 L0150: if ($A <= pread64) goto L0152 L0151: if ($A >= access) goto L0171, else goto L0344 L0152: if ($A <= ioctl) goto L0344 L0153: goto L0574 L0154: if ($A <= fstat) goto L0161 L0155: if ($A <= poll) goto L0160 L0156: if ($A <= mmap) goto L0344 L0157: if ($A >= mprotect) goto L0159 L0158: goto L0594 L0159: goto L0587 L0160: if ($A >= lstat) goto L0171, else goto L0344 L0161: if ($A <= close) goto L0163 L0162: if ($A >= stat) goto L0171, else goto L0344 L0163: if ($A >= open) goto L0171, else goto L0344 L0164: $A = $high_args[3] L0165: if ($A == 0x0) goto L0169 L0166: if ($A != 0xffffffff) goto L0341 L0167: $A = $low_args[3] L0168: if !($A & 0x80000000) goto L0341 L0169: $A = $low_args[3] L0170: if ($A == 0x7ff) goto L0172 L0171: goto L0602 L0172: return ERRNO(38) L0173: $A = $high_args[0] L0174: if ($A == 0x0) goto L0178 L0175: if ($A != 0xffffffff) goto L0341 L0176: $A = $low_args[0] L0177: if !($A & 0x80000000) goto L0341 L0178: $A = $low_args[0] L0179: if ($A == 0x0) goto L0344, else goto L0272 L0180: $A = $high_args[2] L0181: if ($A == 0x0) goto L0185 L0182: if ($A != 0xffffffff) goto L0341 L0183: $A = $low_args[2] L0184: if !($A & 0x80000000) goto L0341 L0185: $A = $low_args[2] L0186: if ($A & 0xfffffffa) goto L0272, else goto L0344 L0187: $A = $high_args[0] L0188: if ($A == 0x0) goto L0192 L0189: if ($A != 0xffffffff) goto L0341 L0190: $A = $low_args[0] L0191: if !($A & 0x80000000) goto L0341 L0192: $A = $low_args[0] L0193: if ($A == 0x0) goto L0344 L0194: $A = $high_args[0] L0195: if ($A == 0x0) goto L0199 L0196: if ($A != 0xffffffff) goto L0341 L0197: $A = $low_args[0] L0198: if !($A & 0x80000000) goto L0341 L0199: $A = $low_args[0] L0200: goto L0416 L0201: $A = $high_args[1] L0202: if ($A == 0x0) goto L0206 L0203: if ($A != 0xffffffff) goto L0341 L0204: $A = $low_args[1] L0205: if !($A & 0x80000000) goto L0341 L0206: $A = $low_args[1] L0207: if ($A & 0xfff7b7ff) goto L0272, else goto L0344 L0208: return TRAP(8) L0209: $A = $high_args[0] L0210: if ($A == 0x0) goto L0214 L0211: if ($A != 0xffffffff) goto L0341 L0212: $A = $low_args[0] L0213: if !($A & 0x80000000) goto L0341 L0214: $A = $low_args[0] L0215: if ($A & 0x80000000) goto L0272 L0216: $A = $high_args[0] L0217: if ($A == 0x0) goto L0221 L0218: if ($A != 0xffffffff) goto L0341 L0219: $A = $low_args[0] L0220: if !($A & 0x80000000) goto L0341 L0221: $A = $low_args[0] L0222: if ($A == 0x7) goto L0344 L0223: $A = $high_args[0] L0224: if ($A == 0x0) goto L0228 L0225: if ($A != 0xffffffff) goto L0341 L0226: $A = $low_args[0] L0227: if !($A & 0x80000000) goto L0341 L0228: $A = $low_args[0] L0229: if ($A == 0x1) goto L0344 L0230: $A = $high_args[0] L0231: if ($A == 0x0) goto L0235 L0232: if ($A != 0xffffffff) goto L0341 L0233: $A = $low_args[0] L0234: if !($A & 0x80000000) goto L0341 L0235: $A = $low_args[0] L0236: if ($A == 0x6) goto L0344 L0237: $A = $high_args[0] L0238: if ($A == 0x0) goto L0242 L0239: if ($A != 0xffffffff) goto L0341 L0240: $A = $low_args[0] L0241: if !($A & 0x80000000) goto L0341 L0242: $A = $low_args[0] L0243: if ($A == 0x4) goto L0344 L0244: $A = $high_args[0] L0245: if ($A == 0x0) goto L0249 L0246: if ($A != 0xffffffff) goto L0341 L0247: $A = $low_args[0] L0248: if !($A & 0x80000000) goto L0341 L0249: $A = $low_args[0] L0250: if ($A == 0x2) goto L0344 L0251: $A = $high_args[0] L0252: if ($A == 0x0) goto L0256 L0253: if ($A != 0xffffffff) goto L0341 L0254: $A = $low_args[0] L0255: if !($A & 0x80000000) goto L0341 L0256: $A = $low_args[0] L0257: if ($A == 0x0) goto L0344 L0258: $A = $high_args[0] L0259: if ($A == 0x0) goto L0263 L0260: if ($A != 0xffffffff) goto L0341 L0261: $A = $low_args[0] L0262: if !($A & 0x80000000) goto L0341 L0263: $A = $low_args[0] L0264: if ($A == 0x5) goto L0344 L0265: $A = $high_args[0] L0266: if ($A == 0x0) goto L0270 L0267: if ($A != 0xffffffff) goto L0341 L0268: $A = $low_args[0] L0269: if !($A & 0x80000000) goto L0341 L0270: $A = $low_args[0] L0271: if ($A == 0x3) goto L0344 L0272: goto L0604 L0273: $A = $high_args[1] L0274: if ($A == 0x0) goto L0278 L0275: if ($A != 0xffffffff) goto L0341 L0276: $A = $low_args[1] L0277: if !($A & 0x80000000) goto L0341 L0278: $A = $low_args[1] L0279: if !($A & 0xfffffe7f) goto L0344 L0280: $A = $high_args[1] L0281: if ($A == 0x0) goto L0285 L0282: if ($A != 0xffffffff) goto L0341 L0283: $A = $low_args[1] L0284: if !($A & 0x80000000) goto L0341 L0285: $A = $low_args[1] L0286: $A &= 0xfffffe7f L0287: if ($A == 0x1) goto L0344 L0288: $A = $high_args[1] L0289: if ($A == 0x0) goto L0293 L0290: if ($A != 0xffffffff) goto L0341 L0291: $A = $low_args[1] L0292: if !($A & 0x80000000) goto L0341 L0293: $A = $low_args[1] L0294: $A &= 0xfffffe7f L0295: if ($A == 0x3) goto L0344 L0296: $A = $high_args[1] L0297: if ($A == 0x0) goto L0301 L0298: if ($A != 0xffffffff) goto L0341 L0299: $A = $low_args[1] L0300: if !($A & 0x80000000) goto L0341 L0301: $A = $low_args[1] L0302: $A &= 0xfffffe7f L0303: if ($A == 0x4) goto L0344 L0304: $A = $high_args[1] L0305: if ($A == 0x0) goto L0309 L0306: if ($A != 0xffffffff) goto L0341 L0307: $A = $low_args[1] L0308: if !($A & 0x80000000) goto L0341 L0309: $A = $low_args[1] L0310: $A &= 0xfffffe7f L0311: if ($A == 0x5) goto L0344 L0312: $A = $high_args[1] L0313: if ($A == 0x0) goto L0317 L0314: if ($A != 0xffffffff) goto L0341 L0315: $A = $low_args[1] L0316: if !($A & 0x80000000) goto L0341 L0317: $A = $low_args[1] L0318: $A &= 0xfffffe7f L0319: if ($A == 0x9) goto L0344 L0320: $A = $high_args[1] L0321: if ($A == 0x0) goto L0325 L0322: if ($A != 0xffffffff) goto L0341 L0323: $A = $low_args[1] L0324: if !($A & 0x80000000) goto L0341 L0325: $A = $low_args[1] L0326: $A &= 0xfffffe7f L0327: if ($A == 0xa) goto L0344 L0328: return ERRNO(22) L0329: $A = $high_args[0] L0330: if ($A == 0x0) goto L0334 L0331: if ($A != 0xffffffff) goto L0341 L0332: $A = $low_args[0] L0333: if !($A & 0x80000000) goto L0341 L0334: $A = $low_args[0] L0335: if ($A == 0x10) goto L0344 L0336: $A = $high_args[0] L0337: if ($A == 0x0) goto L0342 L0338: if ($A != 0xffffffff) goto L0341 L0339: $A = $low_args[0] L0340: if ($A & 0x80000000) goto L0342 L0341: goto L0599 L0342: $A = $low_args[0] L0343: if ($A != 0xf) goto L0345 L0344: goto L0603 L0345: $A = $high_args[0] L0346: if ($A == 0x0) goto L0350 L0347: if ($A != 0xffffffff) goto L0599 L0348: $A = $low_args[0] L0349: if !($A & 0x80000000) goto L0599 L0350: $A = $low_args[0] L0351: if ($A == 0x3) goto L0603 L0352: $A = $high_args[0] L0353: if ($A == 0x0) goto L0357 L0354: if ($A != 0xffffffff) goto L0599 L0355: $A = $low_args[0] L0356: if !($A & 0x80000000) goto L0599 L0357: $A = $low_args[0] L0358: if ($A == 0x4) goto L0603 L0359: $A = $high_args[0] L0360: if ($A == 0x0) goto L0364 L0361: if ($A != 0xffffffff) goto L0599 L0362: $A = $low_args[0] L0363: if !($A & 0x80000000) goto L0599 L0364: $A = $low_args[0] L0365: if ($A == 0x53564d41) goto L0373 L0366: $A = $high_args[0] L0367: if ($A == 0x0) goto L0371 L0368: if ($A != 0xffffffff) goto L0599 L0369: $A = $low_args[0] L0370: if !($A & 0x80000000) goto L0599 L0371: $A = $low_args[0] L0372: if ($A == 0x59616d61) goto L0602, else goto L0380 L0373: $A = $high_args[1] L0374: if ($A == 0x0) goto L0378 L0375: if ($A != 0xffffffff) goto L0599 L0376: $A = $low_args[1] L0377: if !($A & 0x80000000) goto L0599 L0378: $A = $low_args[1] L0379: if ($A == 0x0) goto L0603 L0380: return TRAP(7) L0381: $A = $high_args[0] L0382: if ($A == 0x0) goto L0386 L0383: if ($A != 0xffffffff) goto L0599 L0384: $A = $low_args[0] L0385: if !($A & 0x80000000) goto L0599 L0386: $A = $low_args[0] L0387: if ($A == 0x0) goto L0603 L0388: $A = $high_args[0] L0389: if ($A == 0x0) goto L0393 L0390: if ($A != 0xffffffff) goto L0599 L0391: $A = $low_args[0] L0392: if !($A & 0x80000000) goto L0599 L0393: $A = $low_args[0] L0394: if ($A == 0x1) goto L0603 L0395: return TRAP(6) L0396: $A = $high_args[0] L0397: if ($A == 0x0) goto L0401 L0398: if ($A != 0xffffffff) goto L0599 L0399: $A = $low_args[0] L0400: if !($A & 0x80000000) goto L0599 L0401: $A = $low_args[0] L0402: if ($A != 0x0) goto L0604 L0403: $A = $high_args[1] L0404: if ($A == 0x0) goto L0408 L0405: if ($A != 0xffffffff) goto L0599 L0406: $A = $low_args[1] L0407: if !($A & 0x80000000) goto L0599 L0408: $A = $low_args[1] L0409: if ($A == 0x0) goto L0603 L0410: $A = $high_args[1] L0411: if ($A == 0x0) goto L0415 L0412: if ($A != 0xffffffff) goto L0599 L0413: $A = $low_args[1] L0414: if !($A & 0x80000000) goto L0599 L0415: $A = $low_args[1] L0416: if ($A == 0x1) goto L0603, else goto L0602 L0417: $A = $high_args[1] L0418: if ($A == 0x0) goto L0422 L0419: if ($A != 0xffffffff) goto L0599 L0420: $A = $low_args[1] L0421: if !($A & 0x80000000) goto L0599 L0422: $A = $low_args[1] L0423: if ($A == 0x3) goto L0603 L0424: $A = $high_args[1] L0425: if ($A == 0x0) goto L0429 L0426: if ($A != 0xffffffff) goto L0599 L0427: $A = $low_args[1] L0428: if !($A & 0x80000000) goto L0599 L0429: $A = $low_args[1] L0430: if ($A == 0x1) goto L0603 L0431: $A = $high_args[1] L0432: if ($A == 0x0) goto L0436 L0433: if ($A != 0xffffffff) goto L0599 L0434: $A = $low_args[1] L0435: if !($A & 0x80000000) goto L0599 L0436: $A = $low_args[1] L0437: if ($A == 0x40a) goto L0603 L0438: $A = $high_args[1] L0439: if ($A == 0x0) goto L0443 L0440: if ($A != 0xffffffff) goto L0599 L0441: $A = $low_args[1] L0442: if !($A & 0x80000000) goto L0599 L0443: $A = $low_args[1] L0444: if ($A == 0x2) goto L0603 L0445: $A = $high_args[1] L0446: if ($A == 0x0) goto L0450 L0447: if ($A != 0xffffffff) goto L0599 L0448: $A = $low_args[1] L0449: if !($A & 0x80000000) goto L0599 L0450: $A = $low_args[1] L0451: if ($A == 0x6) goto L0603 L0452: $A = $high_args[1] L0453: if ($A == 0x0) goto L0457 L0454: if ($A != 0xffffffff) goto L0599 L0455: $A = $low_args[1] L0456: if !($A & 0x80000000) goto L0599 L0457: $A = $low_args[1] L0458: if ($A == 0x7) goto L0603 L0459: $A = $high_args[1] L0460: if ($A == 0x0) goto L0464 L0461: if ($A != 0xffffffff) goto L0599 L0462: $A = $low_args[1] L0463: if !($A & 0x80000000) goto L0599 L0464: $A = $low_args[1] L0465: if ($A == 0x5) goto L0603 L0466: $A = $high_args[1] L0467: if ($A == 0x0) goto L0471 L0468: if ($A != 0xffffffff) goto L0599 L0469: $A = $low_args[1] L0470: if !($A & 0x80000000) goto L0599 L0471: $A = $low_args[1] L0472: if ($A == 0x0) goto L0603 L0473: $A = $high_args[1] L0474: if ($A == 0x0) goto L0478 L0475: if ($A != 0xffffffff) goto L0599 L0476: $A = $low_args[1] L0477: if !($A & 0x80000000) goto L0599 L0478: $A = $low_args[1] L0479: if ($A == 0x406) goto L0603 L0480: $A = $high_args[1] L0481: if ($A == 0x0) goto L0485 L0482: if ($A != 0xffffffff) goto L0599 L0483: $A = $low_args[1] L0484: if !($A & 0x80000000) goto L0599 L0485: $A = $low_args[1] L0486: if ($A == 0x4) goto L0496 L0487: $A = $high_args[1] L0488: if ($A == 0x0) goto L0492 L0489: if ($A != 0xffffffff) goto L0599 L0490: $A = $low_args[1] L0491: if !($A & 0x80000000) goto L0599 L0492: $A = $low_args[1] L0493: if ($A != 0x409) goto L0604 L0494: $A = $high_args[2] L0495: if ($A == 0x0) goto L0592, else goto L0604 L0496: $A = $high_args[2] L0497: if ($A != 0x0) goto L0604 L0498: $A = $low_args[2] L0499: if ($A & 0xffe363fc) goto L0604, else goto L0603 L0500: $A = $high_args[0] L0501: if ($A == 0x0) goto L0505 L0502: if ($A != 0xffffffff) goto L0599 L0503: $A = $low_args[0] L0504: if !($A & 0x80000000) goto L0599 L0505: $A = $low_args[0] L0506: if ($A == 0x1) goto L0603 L0507: return TRAP(5) L0508: $A = $high_args[0] L0509: if ($A != 0x0) goto L0512 L0510: $A = $low_args[0] L0511: if ($A == 0x3d0f00) goto L0603 L0512: $A = $low_args[0] L0513: if !($A & 0x10100) goto L0602 L0514: $A = $low_args[0] L0515: $A &= 0x4100 L0516: if ($A == 0x4100) goto L0602 L0517: return TRAP(4) L0518: $A = $high_args[1] L0519: if ($A == 0x0) goto L0523 L0520: if ($A != 0xffffffff) goto L0599 L0521: $A = $low_args[1] L0522: if !($A & 0x80000000) goto L0599 L0523: $A = $low_args[1] L0524: if ($A != 0x1) goto L0604 L0525: $A = $high_args[2] L0526: if ($A == 0x0) goto L0530 L0527: if ($A != 0xffffffff) goto L0599 L0528: $A = $low_args[2] L0529: if !($A & 0x80000000) goto L0599 L0530: $A = $low_args[2] L0531: if ($A == 0x2a) goto L0603, else goto L0604 L0532: $A = $high_args[0] L0533: if ($A == 0x0) goto L0537 L0534: if ($A != 0xffffffff) goto L0599 L0535: $A = $low_args[0] L0536: if !($A & 0x80000000) goto L0599 L0537: $A = $low_args[0] L0538: if ($A == 0x1) goto L0603, else goto L0604 L0539: $A = $high_args[2] L0540: if ($A == 0x0) goto L0544 L0541: if ($A != 0xffffffff) goto L0599 L0542: $A = $low_args[2] L0543: if !($A & 0x80000000) goto L0599 L0544: $A = $low_args[2] L0545: if ($A == 0x4) goto L0603 L0546: $A = $high_args[2] L0547: if ($A == 0x0) goto L0551 L0548: if ($A != 0xffffffff) goto L0599 L0549: $A = $low_args[2] L0550: if !($A & 0x80000000) goto L0599 L0551: $A = $low_args[2] L0552: if ($A == 0x1) goto L0603 L0553: $A = $high_args[2] L0554: if ($A == 0x0) goto L0558 L0555: if ($A != 0xffffffff) goto L0599 L0556: $A = $low_args[2] L0557: if !($A & 0x80000000) goto L0599 L0558: $A = $low_args[2] L0559: if ($A == 0x9) goto L0603 L0560: $A = $high_args[2] L0561: if ($A == 0x0) goto L0565 L0562: if ($A != 0xffffffff) goto L0599 L0563: $A = $low_args[2] L0564: if !($A & 0x80000000) goto L0599 L0565: $A = $low_args[2] L0566: if ($A == 0x0) goto L0603 L0567: $A = $high_args[2] L0568: if ($A == 0x0) goto L0572 L0569: if ($A != 0xffffffff) goto L0599 L0570: $A = $low_args[2] L0571: if !($A & 0x80000000) goto L0599 L0572: $A = $low_args[2] L0573: if ($A == 0x8) goto L0603, else goto L0602 L0574: $A = $high_args[1] L0575: if ($A != 0x0) goto L0578 L0576: $A = $low_args[1] L0577: if ($A == 0x5401) goto L0603 L0578: $A = $high_args[1] L0579: if ($A != 0x0) goto L0582 L0580: $A = $low_args[1] L0581: if ($A == 0x541b) goto L0603 L0582: $A = $high_args[1] L0583: if ($A != 0x0) goto L0586 L0584: $A = $low_args[1] L0585: if ($A == 0x40086200) goto L0603 L0586: return TRAP(3) L0587: $A = $high_args[2] L0588: if ($A == 0x0) goto L0592 L0589: if ($A != 0xffffffff) goto L0599 L0590: $A = $low_args[2] L0591: if !($A & 0x80000000) goto L0599 L0592: $A = $low_args[2] L0593: if ($A & 0xfffffff8) goto L0604, else goto L0603 L0594: $A = $high_args[3] L0595: if ($A == 0x0) goto L0600 L0596: if ($A != 0xffffffff) goto L0599 L0597: $A = $low_args[3] L0598: if ($A & 0x80000000) goto L0600 L0599: return TRAP(2) L0600: $A = $low_args[3] L0601: if ($A & 0xfffd97cc) goto L0604, else goto L0603 L0602: return ERRNO(1) #... 3 line(s) skipped ceccomp-4.0/test/emu_result/gctf-2019-quals-caas.accept000066400000000000000000000053061514205130000226510ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0063 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0063 L0005: if ($A == read) goto L0062 L0006: if ($A == write) goto L0062 L0007: if ($A == close) goto L0062 L0008: if ($A == munmap) goto L0062 L0009: if ($A == sched_yield) goto L0062 L0010: if ($A == dup) goto L0062 L0011: if ($A == dup2) goto L0062 L0012: if ($A == nanosleep) goto L0062 L0013: if ($A == connect) goto L0062 L0014: if ($A == accept) goto L0062 L0015: if ($A == recvmsg) goto L0062 L0016: if ($A == bind) goto L0062 L0017: if ($A == exit) goto L0062 L0018: if ($A == exit_group) goto L0062 L0019: if ($A != clone) goto L0024 L0020: $A = $high_args[0] L0021: if ($A != 0x0) goto L0063 L0022: $A = $low_args[0] L0023: if ($A == 0x10900) goto L0062, else goto L0063 L0024: if ($A != socket) goto L0037 L0025: $A = $high_args[0] L0026: if ($A != 0x0) goto L0063 L0027: $A = $low_args[0] L0028: if ($A != 0x2) goto L0063 L0029: $A = $high_args[1] L0030: if ($A != 0x0) goto L0063 L0031: $A = $low_args[1] L0032: if ($A != 0x1) goto L0063 L0033: $A = $high_args[2] L0034: if ($A != 0x0) goto L0063 L0035: $A = $low_args[2] L0036: if ($A == 0x0) goto L0062, else goto L0063 L0037: if ($A != mmap) goto L0063 L0038: $A = $high_args[0] L0039: if ($A != 0x0) goto L0063 L0040: $A = $low_args[0] L0041: if ($A != 0x0) goto L0063 L0042: $A = $high_args[1] L0043: if ($A != 0x0) goto L0063 L0044: $A = $low_args[1] L0045: if ($A != 0x1000) goto L0063 L0046: $A = $high_args[2] L0047: if ($A != 0x0) goto L0063 L0048: $A = $low_args[2] L0049: if ($A != 0x3) goto L0063 L0050: $A = $high_args[3] L0051: if ($A != 0x0) goto L0063 L0052: $A = $low_args[3] L0053: if ($A != 0x22) goto L0063 L0054: $A = $high_args[4] L0055: if ($A != 0x0) goto L0063 L0056: $A = $low_args[4] L0057: if ($A != 0x0) goto L0063 L0058: $A = $high_args[5] L0059: if ($A != 0x0) goto L0063 L0060: $A = $low_args[5] L0061: if ($A != 0x0) goto L0063 L0062: return ALLOW #... 2 line(s) skipped ceccomp-4.0/test/emu_result/gctf-2019-quals-caas.open000066400000000000000000000054411514205130000223530ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0063 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0063 L0005: if ($A == read) goto L0062 L0006: if ($A == write) goto L0062 L0007: if ($A == close) goto L0062 L0008: if ($A == munmap) goto L0062 L0009: if ($A == sched_yield) goto L0062 L0010: if ($A == dup) goto L0062 L0011: if ($A == dup2) goto L0062 L0012: if ($A == nanosleep) goto L0062 L0013: if ($A == connect) goto L0062 L0014: if ($A == accept) goto L0062 L0015: if ($A == recvmsg) goto L0062 L0016: if ($A == bind) goto L0062 L0017: if ($A == exit) goto L0062 L0018: if ($A == exit_group) goto L0062 L0019: if ($A != clone) goto L0024 L0020: $A = $high_args[0] L0021: if ($A != 0x0) goto L0063 L0022: $A = $low_args[0] L0023: if ($A == 0x10900) goto L0062, else goto L0063 L0024: if ($A != socket) goto L0037 L0025: $A = $high_args[0] L0026: if ($A != 0x0) goto L0063 L0027: $A = $low_args[0] L0028: if ($A != 0x2) goto L0063 L0029: $A = $high_args[1] L0030: if ($A != 0x0) goto L0063 L0031: $A = $low_args[1] L0032: if ($A != 0x1) goto L0063 L0033: $A = $high_args[2] L0034: if ($A != 0x0) goto L0063 L0035: $A = $low_args[2] L0036: if ($A == 0x0) goto L0062, else goto L0063 L0037: if ($A != mmap) goto L0063 L0038: $A = $high_args[0] L0039: if ($A != 0x0) goto L0063 L0040: $A = $low_args[0] L0041: if ($A != 0x0) goto L0063 L0042: $A = $high_args[1] L0043: if ($A != 0x0) goto L0063 L0044: $A = $low_args[1] L0045: if ($A != 0x1000) goto L0063 L0046: $A = $high_args[2] L0047: if ($A != 0x0) goto L0063 L0048: $A = $low_args[2] L0049: if ($A != 0x3) goto L0063 L0050: $A = $high_args[3] L0051: if ($A != 0x0) goto L0063 L0052: $A = $low_args[3] L0053: if ($A != 0x22) goto L0063 L0054: $A = $high_args[4] L0055: if ($A != 0x0) goto L0063 L0056: $A = $low_args[4] L0057: if ($A != 0x0) goto L0063 L0058: $A = $high_args[5] L0059: if ($A != 0x0) goto L0063 L0060: $A = $low_args[5] L0061: if ($A != 0x0) goto L0063 L0062: return ALLOW L0063: return KILL #... 1 line(s) skipped ceccomp-4.0/test/emu_result/gctf-2019-quals-caas.pipe000066400000000000000000000054411514205130000223470ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0063 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0063 L0005: if ($A == read) goto L0062 L0006: if ($A == write) goto L0062 L0007: if ($A == close) goto L0062 L0008: if ($A == munmap) goto L0062 L0009: if ($A == sched_yield) goto L0062 L0010: if ($A == dup) goto L0062 L0011: if ($A == dup2) goto L0062 L0012: if ($A == nanosleep) goto L0062 L0013: if ($A == connect) goto L0062 L0014: if ($A == accept) goto L0062 L0015: if ($A == recvmsg) goto L0062 L0016: if ($A == bind) goto L0062 L0017: if ($A == exit) goto L0062 L0018: if ($A == exit_group) goto L0062 L0019: if ($A != clone) goto L0024 L0020: $A = $high_args[0] L0021: if ($A != 0x0) goto L0063 L0022: $A = $low_args[0] L0023: if ($A == 0x10900) goto L0062, else goto L0063 L0024: if ($A != socket) goto L0037 L0025: $A = $high_args[0] L0026: if ($A != 0x0) goto L0063 L0027: $A = $low_args[0] L0028: if ($A != 0x2) goto L0063 L0029: $A = $high_args[1] L0030: if ($A != 0x0) goto L0063 L0031: $A = $low_args[1] L0032: if ($A != 0x1) goto L0063 L0033: $A = $high_args[2] L0034: if ($A != 0x0) goto L0063 L0035: $A = $low_args[2] L0036: if ($A == 0x0) goto L0062, else goto L0063 L0037: if ($A != mmap) goto L0063 L0038: $A = $high_args[0] L0039: if ($A != 0x0) goto L0063 L0040: $A = $low_args[0] L0041: if ($A != 0x0) goto L0063 L0042: $A = $high_args[1] L0043: if ($A != 0x0) goto L0063 L0044: $A = $low_args[1] L0045: if ($A != 0x1000) goto L0063 L0046: $A = $high_args[2] L0047: if ($A != 0x0) goto L0063 L0048: $A = $low_args[2] L0049: if ($A != 0x3) goto L0063 L0050: $A = $high_args[3] L0051: if ($A != 0x0) goto L0063 L0052: $A = $low_args[3] L0053: if ($A != 0x22) goto L0063 L0054: $A = $high_args[4] L0055: if ($A != 0x0) goto L0063 L0056: $A = $low_args[4] L0057: if ($A != 0x0) goto L0063 L0058: $A = $high_args[5] L0059: if ($A != 0x0) goto L0063 L0060: $A = $low_args[5] L0061: if ($A != 0x0) goto L0063 L0062: return ALLOW L0063: return KILL #... 1 line(s) skipped ceccomp-4.0/test/emu_result/ld_alu_misc.accept000066400000000000000000000007161514205130000215600ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $low_pc L0002: $X = $A L0003: $A = $high_pc L0004: $A = $high_args[0] L0005: $A = $low_args[5] L0006: $A += $X L0007: $A &= 0xfff L0008: if ($A == 0x0) goto L0010 L0009: return KILL #... 14 line(s) skipped ceccomp-4.0/test/emu_result/ld_alu_misc.open000066400000000000000000000007161514205130000212620ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $low_pc L0002: $X = $A L0003: $A = $high_pc L0004: $A = $high_args[0] L0005: $A = $low_args[5] L0006: $A += $X L0007: $A &= 0xfff L0008: if ($A == 0x0) goto L0010 L0009: return KILL #... 14 line(s) skipped ceccomp-4.0/test/emu_result/ld_alu_misc.pipe000066400000000000000000000007161514205130000212560ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $low_pc L0002: $X = $A L0003: $A = $high_pc L0004: $A = $high_args[0] L0005: $A = $low_args[5] L0006: $A += $X L0007: $A &= 0xfff L0008: if ($A == 0x0) goto L0010 L0009: return KILL #... 14 line(s) skipped ceccomp-4.0/test/emu_result/libseccomp.accept000066400000000000000000000010471514205130000214230ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0011 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0011 L0005: if ($A == execveat) goto L0010 L0006: if ($A == execve) goto L0010 L0007: if ($A == fcntl) goto L0010 L0008: if ($A == exit) goto L0010 L0009: return ERRNO(5) #... 3 line(s) skipped ceccomp-4.0/test/emu_result/libseccomp.open000066400000000000000000000010471514205130000211250ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0011 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0011 L0005: if ($A == execveat) goto L0010 L0006: if ($A == execve) goto L0010 L0007: if ($A == fcntl) goto L0010 L0008: if ($A == exit) goto L0010 L0009: return ERRNO(5) #... 3 line(s) skipped ceccomp-4.0/test/emu_result/libseccomp.pipe000066400000000000000000000010471514205130000211210ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0011 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0011 L0005: if ($A == execveat) goto L0010 L0006: if ($A == execve) goto L0010 L0007: if ($A == fcntl) goto L0010 L0008: if ($A == exit) goto L0010 L0009: return ERRNO(5) #... 3 line(s) skipped ceccomp-4.0/test/emu_result/multi_arch.accept000066400000000000000000000023411514205130000214300ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0008 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0023 L0005: if ($A == accept) goto L0024 L0006: if ($A == listen) goto L0024 L0007: if ($A == bind) goto L0024, else goto L0023 L0008: if ($A != i386) goto L0013 L0009: $A = $syscall_nr L0010: if ($A == i386.restart_syscall) goto L0024 L0011: if ($A == i386.exit) goto L0024 L0012: if ($A == i386.fork) goto L0024, else goto L0023 L0013: if ($A != aarch64) goto L0018 L0014: $A = $syscall_nr L0015: if ($A == aarch64.alarm) goto L0024 L0016: if ($A == aarch64.select) goto L0024 L0017: if ($A == aarch64.pipe) goto L0024, else goto L0023 L0018: if ($A != arm) goto L0023 L0019: $A = $syscall_nr L0020: if ($A == arm.restart_syscall) goto L0024 L0021: if ($A == arm.readv) goto L0024 L0022: if ($A == arm.clone) goto L0024 L0023: return KILL L0024: return ALLOW #... 1 line(s) skipped ceccomp-4.0/test/emu_result/multi_arch.open000066400000000000000000000023261514205130000211350ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0008 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0023 L0005: if ($A == accept) goto L0024 L0006: if ($A == listen) goto L0024 L0007: if ($A == bind) goto L0024, else goto L0023 L0008: if ($A != i386) goto L0013 L0009: $A = $syscall_nr L0010: if ($A == i386.restart_syscall) goto L0024 L0011: if ($A == i386.exit) goto L0024 L0012: if ($A == i386.fork) goto L0024, else goto L0023 L0013: if ($A != aarch64) goto L0018 L0014: $A = $syscall_nr L0015: if ($A == aarch64.alarm) goto L0024 L0016: if ($A == aarch64.select) goto L0024 L0017: if ($A == aarch64.pipe) goto L0024, else goto L0023 L0018: if ($A != arm) goto L0023 L0019: $A = $syscall_nr L0020: if ($A == arm.restart_syscall) goto L0024 L0021: if ($A == arm.readv) goto L0024 L0022: if ($A == arm.clone) goto L0024 L0023: return KILL #... 2 line(s) skipped ceccomp-4.0/test/emu_result/multi_arch.pipe000066400000000000000000000023261514205130000211310ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != x86_64) goto L0008 L0003: $A = $syscall_nr L0004: if ($A >= 0x40000000) goto L0023 L0005: if ($A == accept) goto L0024 L0006: if ($A == listen) goto L0024 L0007: if ($A == bind) goto L0024, else goto L0023 L0008: if ($A != i386) goto L0013 L0009: $A = $syscall_nr L0010: if ($A == i386.restart_syscall) goto L0024 L0011: if ($A == i386.exit) goto L0024 L0012: if ($A == i386.fork) goto L0024, else goto L0023 L0013: if ($A != aarch64) goto L0018 L0014: $A = $syscall_nr L0015: if ($A == aarch64.alarm) goto L0024 L0016: if ($A == aarch64.select) goto L0024 L0017: if ($A == aarch64.pipe) goto L0024, else goto L0023 L0018: if ($A != arm) goto L0023 L0019: $A = $syscall_nr L0020: if ($A == arm.restart_syscall) goto L0024 L0021: if ($A == arm.readv) goto L0024 L0022: if ($A == arm.clone) goto L0024 L0023: return KILL #... 2 line(s) skipped ceccomp-4.0/test/emu_result/twctf-2016-diary.accept000066400000000000000000000015041514205130000221240ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $syscall_nr L0002: if ($A != open) goto L0004 L0003: return KILL L0004: if ($A != openat) goto L0006 L0005: return KILL L0006: if ($A != execve) goto L0008 L0007: return KILL L0008: if ($A != clone) goto L0010 L0009: return KILL L0010: if ($A != fork) goto L0012 L0011: return KILL L0012: if ($A != vfork) goto L0014 L0013: return KILL L0014: if ($A != creat) goto L0016 L0015: return KILL L0016: if ($A != execveat) goto L0018 L0017: return KILL L0018: return ALLOW #... 1 line(s) skipped ceccomp-4.0/test/emu_result/twctf-2016-diary.open000066400000000000000000000003551514205130000216310ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $syscall_nr L0002: if ($A != open) goto L0004 L0003: return KILL #... 16 line(s) skipped ceccomp-4.0/test/emu_result/twctf-2016-diary.pipe000066400000000000000000000015041514205130000216220ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $syscall_nr L0002: if ($A != open) goto L0004 L0003: return KILL L0004: if ($A != openat) goto L0006 L0005: return KILL L0006: if ($A != execve) goto L0008 L0007: return KILL L0008: if ($A != clone) goto L0010 L0009: return KILL L0010: if ($A != fork) goto L0012 L0011: return KILL L0012: if ($A != vfork) goto L0014 L0013: return KILL L0014: if ($A != creat) goto L0016 L0015: return KILL L0016: if ($A != execveat) goto L0018 L0017: return KILL L0018: return ALLOW #... 1 line(s) skipped ceccomp-4.0/test/emu_result/x32.accept000066400000000000000000000011511514205130000177130ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != i386) goto L0012 L0003: $A = $syscall_nr L0004: if ($A <= 0x40000000) goto L0012 L0005: if ($A == 0x40000000) goto L0012 L0006: if ($A == 0x40000001) goto L0012 L0007: if ($A == 0x400000ac) goto L0012 L0008: if ($A != 0x40000009) goto L0012 L0009: $A = $low_args[0] L0010: if ($A == 0x0) goto L0012 L0011: return ERRNO(5) L0012: return ALLOW #... 1 line(s) skipped ceccomp-4.0/test/emu_result/x32.open000066400000000000000000000011511514205130000174150ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != i386) goto L0012 L0003: $A = $syscall_nr L0004: if ($A <= 0x40000000) goto L0012 L0005: if ($A == 0x40000000) goto L0012 L0006: if ($A == 0x40000001) goto L0012 L0007: if ($A == 0x400000ac) goto L0012 L0008: if ($A != 0x40000009) goto L0012 L0009: $A = $low_args[0] L0010: if ($A == 0x0) goto L0012 L0011: return ERRNO(5) L0012: return ALLOW #... 1 line(s) skipped ceccomp-4.0/test/emu_result/x32.pipe000066400000000000000000000011511514205130000174110ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: $A = $arch L0002: if ($A != i386) goto L0012 L0003: $A = $syscall_nr L0004: if ($A <= 0x40000000) goto L0012 L0005: if ($A == 0x40000000) goto L0012 L0006: if ($A == 0x40000001) goto L0012 L0007: if ($A == 0x400000ac) goto L0012 L0008: if ($A != 0x40000009) goto L0012 L0009: $A = $low_args[0] L0010: if ($A == 0x0) goto L0012 L0011: return ERRNO(5) L0012: return ALLOW #... 1 line(s) skipped ceccomp-4.0/test/errors/000077500000000000000000000000001514205130000152505ustar00rootroot00000000000000ceccomp-4.0/test/errors/a01-unexpected-token000066400000000000000000000003621514205130000210350ustar00rootroot00000000000000Test unexpected token and number overflow STDIN $K = 0 $X = 0xffffffffff return ALLOW STDERR [WARN]: 1:1: Unexpected token $K = 0 ^ [WARN]: 2:6: Input number exceeds 32-bit range $X = 0xffffffffff ^ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/a02-labels000066400000000000000000000004361514205130000170200ustar00rootroot00000000000000Test duplicated labels and labels that are not found STDIN L1: if ($A == 0) goto L2 L1: return ALLOW STDERR [WARN]: 2:-: Can not find label declaration if ($A == 0) goto L2 ~~~~~~~~~~~~~~~~~~~~ [WARN]: 3:1: Found duplicated label declaration L1: ^ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/a03-return000066400000000000000000000010631514205130000170730ustar00rootroot00000000000000Test return related errors STDIN return ERRN return ERRNO return ERRNO( return ERRNO(2 return KILL(1) return return TRACE(10000000) STDERR [WARN]: 1:8: Expect return value return ERRN ^ [WARN]: 3:14: Expect number return ERRNO( ^ [WARN]: 4:15: Expect parenthesis return ERRNO(2 ^ [WARN]: 5:12: Unexpected token return KILL(1) ^ [WARN]: 6:7: Expect return value return ^ [WARN]: 7:-: Data carried by return motion exceeds 0xffff return TRACE(10000000) ~~~~~~~~~~~~~~~~~~~~~~ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/a04-assign-line000066400000000000000000000037521514205130000177750ustar00rootroot00000000000000Test assign line related errors STDIN $A = 0 $A = $X $A = $mem[0] $A = $mem[16] $A = $scmp_data_len $A = $arch $A = $low_args[ $A = $high_args[5 $A = $high_args[0x10] $X = 0 $X = $A $X = $mem[ $X = $mem[1] $X = $mem[16] $X = $scmp_data_len $X = $syscall_nr $X -= 0 $X = -$X $mem = $A $mem[4] = 0 $mem[5] = $A $mem[6] = $X $mem[0] = $mem[0] $A = $mem[5] $mem[7] = $syscall_nr $mem[5] -= 0 $A $A - 0 $A = $A + 0 $A = $A $X = $X $A -= 0 $A =- 0 $A =- $A $A = -$X $A += $mem[4] $A *= $X return $A STDERR [WARN]: 3:-: Accessing uninitialized mem $A = $mem[0] ~~~~~~~~~~~~ [WARN]: 4:-: Mem index out of range (0-15) $A = $mem[16] ~~~~~~~~~~~~~ [WARN]: 7:16: Expect number $A = $low_args[ ^ [WARN]: 8:18: Expect bracket $A = $high_args[5 ^ [WARN]: 9:-: Args index out of range (0-5) $A = $high_args[0x10] ~~~~~~~~~~~~~~~~~~~~~ [WARN]: 13:11: Expect number $X = $mem[ ^ [WARN]: 14:-: Accessing uninitialized mem $X = $mem[1] ~~~~~~~~~~~~ [WARN]: 15:-: Mem index out of range (0-15) $X = $mem[16] ~~~~~~~~~~~~~ [WARN]: 17:-: Lvalue should be '$A' $X = $syscall_nr ~~~~~~~~~~~~~~~~ [WARN]: 18:-: Operator should be '=' $X -= 0 ~~~~~~~ [WARN]: 19:-: Operator should be '=' $X = -$X ~~~~~~~~ [WARN]: 21:6: Expect bracket $mem = $A ^ [WARN]: 22:-: Rvalue should be '$A' or '$X' $mem[4] = 0 ~~~~~~~~~~~ [WARN]: 25:-: Rvalue should be '$A' or '$X' $mem[0] = $mem[0] ~~~~~~~~~~~~~~~~~ [WARN]: 27:-: Rvalue should be '$A' or '$X' $mem[7] = $syscall_nr ~~~~~~~~~~~~~~~~~~~~~ [WARN]: 28:-: Operator should be '=' $mem[5] -= 0 ~~~~~~~~~~~~ [WARN]: 30:3: Expect operator $A ^ [WARN]: 31:4: Expect operator $A - 0 ^ [WARN]: 32:9: Unexpected token $A = $A + 0 ^ [WARN]: 33:-: Rvalue can not be '$A' $A = $A ~~~~~~~ [WARN]: 34:-: Rvalue can not be '$X' $X = $X ~~~~~~~ [WARN]: 36:-: Rvalue should be '$A' $A =- 0 ~~~~~~~ [WARN]: 38:-: Rvalue should be '$A' $A = -$X ~~~~~~~~ [WARN]: 39:-: Rvalue should be '$X' or number $A += $mem[4] ~~~~~~~~~~~~~ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/a05-jump-line000066400000000000000000000140721514205130000174620ustar00rootroot00000000000000Test jump line related errors STDIN goto far goto outside goto 333 reverse: if ($A == 0) goto end if !($A == 0) goto end if !!($A == 0) goto end if $A if ($X) if ($A - 0) if ($A & $X) goto end if ($A != noidea) goto end if ($A == i386) goto end if ($A == ?) if ($A == i386.) goto end if ($A == 0) $A if ($A == 0) goto end, if ($A == 0) goto end, else if ($A == 0) goto reverse if ($A == 0) goto end, else goto reverse if ($A == 0) goto far if ($A != 0) goto end, else goto outside end: return ERRNO(1) return ERRNO(2) return ERRNO(3) return ERRNO(4) return ERRNO(5) return ERRNO(6) return ERRNO(7) return ERRNO(8) return ERRNO(9) return ERRNO(10) return ERRNO(11) return ERRNO(12) return ERRNO(13) return ERRNO(14) return ERRNO(15) return ERRNO(16) return ERRNO(17) return ERRNO(18) return ERRNO(19) return ERRNO(20) return ERRNO(21) return ERRNO(22) return ERRNO(23) return ERRNO(24) return ERRNO(25) return ERRNO(26) return ERRNO(27) return ERRNO(28) return ERRNO(29) return ERRNO(30) return ERRNO(31) return ERRNO(32) return ERRNO(33) return ERRNO(34) return ERRNO(35) return ERRNO(36) return ERRNO(37) return ERRNO(38) return ERRNO(39) return ERRNO(40) return ERRNO(41) return ERRNO(42) return ERRNO(43) return ERRNO(44) return ERRNO(45) return ERRNO(46) return ERRNO(47) return ERRNO(48) return ERRNO(49) return ERRNO(50) return ERRNO(51) return ERRNO(52) return ERRNO(53) return ERRNO(54) return ERRNO(55) return ERRNO(56) return ERRNO(57) return ERRNO(58) return ERRNO(59) return ERRNO(60) return ERRNO(61) return ERRNO(62) return ERRNO(63) return ERRNO(64) return ERRNO(65) return ERRNO(66) return ERRNO(67) return ERRNO(68) return ERRNO(69) return ERRNO(70) return ERRNO(71) return ERRNO(72) return ERRNO(73) return ERRNO(74) return ERRNO(75) return ERRNO(76) return ERRNO(77) return ERRNO(78) return ERRNO(79) return ERRNO(80) return ERRNO(81) return ERRNO(82) return ERRNO(83) return ERRNO(84) return ERRNO(85) return ERRNO(86) return ERRNO(87) return ERRNO(88) return ERRNO(89) return ERRNO(90) return ERRNO(91) return ERRNO(92) return ERRNO(93) return ERRNO(94) return ERRNO(95) return ERRNO(96) return ERRNO(97) return ERRNO(98) return ERRNO(99) return ERRNO(100) return ERRNO(101) return ERRNO(102) return ERRNO(103) return ERRNO(104) return ERRNO(105) return ERRNO(106) return ERRNO(107) return ERRNO(108) return ERRNO(109) return ERRNO(110) return ERRNO(111) return ERRNO(112) return ERRNO(113) return ERRNO(114) return ERRNO(115) return ERRNO(116) return ERRNO(117) return ERRNO(118) return ERRNO(119) return ERRNO(120) return ERRNO(121) return ERRNO(122) return ERRNO(123) return ERRNO(124) return ERRNO(125) return ERRNO(126) return ERRNO(127) return ERRNO(128) return ERRNO(129) return ERRNO(130) return ERRNO(131) return ERRNO(132) return ERRNO(133) return ERRNO(134) return ERRNO(135) return ERRNO(136) return ERRNO(137) return ERRNO(138) return ERRNO(139) return ERRNO(140) return ERRNO(141) return ERRNO(142) return ERRNO(143) return ERRNO(144) return ERRNO(145) return ERRNO(146) return ERRNO(147) return ERRNO(148) return ERRNO(149) return ERRNO(150) return ERRNO(151) return ERRNO(152) return ERRNO(153) return ERRNO(154) return ERRNO(155) return ERRNO(156) return ERRNO(157) return ERRNO(158) return ERRNO(159) return ERRNO(160) return ERRNO(161) return ERRNO(162) return ERRNO(163) return ERRNO(164) return ERRNO(165) return ERRNO(166) return ERRNO(167) return ERRNO(168) return ERRNO(169) return ERRNO(170) return ERRNO(171) return ERRNO(172) return ERRNO(173) return ERRNO(174) return ERRNO(175) return ERRNO(176) return ERRNO(177) return ERRNO(178) return ERRNO(179) return ERRNO(180) return ERRNO(181) return ERRNO(182) return ERRNO(183) return ERRNO(184) return ERRNO(185) return ERRNO(186) return ERRNO(187) return ERRNO(188) return ERRNO(189) return ERRNO(190) return ERRNO(191) return ERRNO(192) return ERRNO(193) return ERRNO(194) return ERRNO(195) return ERRNO(196) return ERRNO(197) return ERRNO(198) return ERRNO(199) return ERRNO(200) return ERRNO(201) return ERRNO(202) return ERRNO(203) return ERRNO(204) return ERRNO(205) return ERRNO(206) return ERRNO(207) return ERRNO(208) return ERRNO(209) return ERRNO(210) return ERRNO(211) return ERRNO(212) return ERRNO(213) return ERRNO(214) return ERRNO(215) return ERRNO(216) return ERRNO(217) return ERRNO(218) return ERRNO(219) return ERRNO(220) return ERRNO(221) return ERRNO(222) return ERRNO(223) return ERRNO(224) return ERRNO(225) return ERRNO(226) return ERRNO(227) return ERRNO(228) return ERRNO(229) return ERRNO(230) return ERRNO(231) return ERRNO(232) return ERRNO(233) return ERRNO(234) return ERRNO(235) return ERRNO(236) return ERRNO(237) return ERRNO(238) return ERRNO(239) return ERRNO(240) return ERRNO(241) return ERRNO(242) return ERRNO(243) return ERRNO(244) return ERRNO(245) return ERRNO(246) return ERRNO(247) return ERRNO(248) return ERRNO(249) return ERRNO(250) return ERRNO(251) return ERRNO(252) return ERRNO(253) return ERRNO(254) return ERRNO(255) return ERRNO(256) far:return KILL outside: STDERR [WARN]: 2:-: JA out of filters goto outside ~~~~~~~~~~~~ [WARN]: 3:6: Expect label goto 333 ^ [WARN]: 8:5: Expect parenthesis if !!($A == 0) goto end ^ [WARN]: 9:4: Expect parenthesis if $A ^ [WARN]: 10:5: Expect '$A' if ($X) ^ [WARN]: 11:8: Expect comparator if ($A - 0) ^ [WARN]: 13:11: Expect valid syscall name if ($A != noidea) goto end ^ [WARN]: 15:11: Unexpected token if ($A == ?) ^ [WARN]: 16:16: Expect valid syscall name if ($A == i386.) goto end ^ [WARN]: 17:14: Expect 'goto' if ($A == 0) $A ^ [WARN]: 18:23: Expect 'else' if ($A == 0) goto end, ^ [WARN]: 19:28: Expect 'goto' if ($A == 0) goto end, else ^ [WARN]: 21:-: JT must be positive if ($A == 0) goto reverse ~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: 22:-: JF must be positive if ($A == 0) goto end, else goto reverse ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: 23:-: JT is larger than 0xff if ($A == 0) goto far ~~~~~~~~~~~~~~~~~~~~~ [WARN]: 24:-: JF is larger than 0xff if ($A != 0) goto end, else goto outside ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/a06-jt-jf-out000066400000000000000000000005601514205130000173770ustar00rootroot00000000000000Test JT and JF out of filters STDIN if ($A > 0) goto outside if ($A <= 0) goto end, else goto outside end: return ALLOW outside: STDERR [WARN]: 1:-: JT out of filters if ($A > 0) goto outside ~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: 2:-: JF out of filters if ($A <= 0) goto end, else goto outside ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/a07-alu000066400000000000000000000004771514205130000163510ustar00rootroot00000000000000Test alu related errors STDIN $A <<= 64 $A >>= 0x100 $A /= 0 return ALLOW STDERR [WARN]: 1:-: Left or right shifting more than 32 bits $A <<= 64 ~~~~~~~~~ [WARN]: 2:-: Left or right shifting more than 32 bits $A >>= 0x100 ~~~~~~~~~~~~ [WARN]: 3:-: Dividing by zero $A /= 0 ~~~~~~~ [ERROR]: Found errors when assembling ceccomp-4.0/test/errors/b01-invalid-operation000066400000000000000000000020501514205130000211740ustar00rootroot00000000000000Test 5+ invalid operations with normal operations STDIN 00 00 00 00 00 00 00 00 ff ff 00 ff 00 ff 00 ff 13 37 13 37 13 37 13 37 de ad be ef de ad be ef ca fe ba be ca fe ba be 7f 45 4c 46 7f 45 4c 46 fe dc ba 98 76 54 32 10 ff ee dd cc bb aa 99 88 06 00 00 00 00 00 ff 7f STDERR [WARN]: #2: Invalid or unknown operation CODE:0xffff JT:0x00 JF:0xff K:0xff00ff00 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: #3: Invalid or unknown operation CODE:0x3713 JT:0x13 JF:0x37 K:0x37133713 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: #4: Invalid or unknown operation CODE:0xadde JT:0xbe JF:0xef K:0xefbeadde ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: #5: Invalid or unknown operation CODE:0xfeca JT:0xba JF:0xbe K:0xbebafeca ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: #6: Invalid or unknown operation CODE:0x457f JT:0x4c JF:0x46 K:0x464c457f ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [WARN]: #7: Invalid or unknown operation CODE:0xdcfe JT:0xba JF:0x98 K:0x10325476 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [ERROR]: Found fatal errors when disassembling ceccomp-4.0/test/errors/b02-invalid-load-offset000066400000000000000000000006731514205130000214110ustar00rootroot00000000000000Test invalid load offset with normal operations STDIN 20 00 00 00 05 00 00 00 20 00 00 00 40 00 00 00 06 00 00 00 00 00 ff 7f STDERR [WARN]: #1: seccomp_data load offset must be 4-byte aligned CODE:0x0021 JT:0x00 JF:0x00 K:0x00000005 ^ [WARN]: #2: seccomp_data load offset greater than the struct CODE:0x0021 JT:0x00 JF:0x00 K:0x00000040 ^ [ERROR]: Found fatal errors when disassembling ceccomp-4.0/test/errors/b03-alu-error000066400000000000000000000020141514205130000174620ustar00rootroot00000000000000Test ALU loading errors and index overflow and no return STDIN 60 00 00 00 00 00 00 00 60 00 00 00 10 00 00 00 34 00 00 00 00 00 00 00 64 00 00 00 7f 00 00 00 STDERR [WARN]: #1: Accessing uninitialized mem CODE:0x0060 JT:0x00 JF:0x00 K:0x00000000 ^ [WARN]: #2: Mem index out of range (0-15) CODE:0x0060 JT:0x00 JF:0x00 K:0x00000010 ^ [WARN]: #3: Dividing by zero CODE:0x0034 JT:0x00 JF:0x00 K:0x00000000 ^ [WARN]: #4: Left or right shifting more than 32 bits CODE:0x0064 JT:0x00 JF:0x00 K:0x0000007f ^ [WARN]: #4: BPF filters must end with return CODE:0x0064 JT:0x00 JF:0x00 K:0x0000007f ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STDOUT #Label CODE JT JF K #--------------------------------- L0001: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0002: 0x60 0x00 0x00 0x00000010 $A = $mem[0x10] L0003: 0x34 0x00 0x00 0x00000000 $A /= 0x0 L0004: 0x64 0x00 0x00 0x0000007f $A <<= 0x7f #--------------------------------- ceccomp-4.0/test/errors/b04-jump-error000066400000000000000000000016301514205130000176600ustar00rootroot00000000000000Test jumping out of filters STDIN 05 00 00 00 00 30 00 00 15 00 09 00 00 00 00 00 15 00 00 21 00 00 00 00 15 00 ee dd 00 00 00 00 06 00 00 00 00 00 ff 7f STDERR [WARN]: #1: JA out of filters CODE:0x0005 JT:0x00 JF:0x00 K:0x00003000 ^ [WARN]: #2: JT out of filters CODE:0x0015 JT:0x09 JF:0x00 K:0x00000000 ^ [WARN]: #3: JF out of filters CODE:0x0015 JT:0x00 JF:0x21 K:0x00000000 ^ [WARN]: #4: JT out of filters CODE:0x0015 JT:0xee JF:0xdd K:0x00000000 ^ STDOUT #Label CODE JT JF K #--------------------------------- L0001: 0x05 0x00 0x00 0x00003000 goto L12290 L0002: 0x15 0x09 0x00 0x00000000 if ($A == 0x0) goto L0012 L0003: 0x15 0x00 0x21 0x00000000 if ($A != 0x0) goto L0037 L0004: 0x15 0xee 0xdd 0x00000000 if ($A == 0x0) goto L0243, else goto L0226 L0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- ceccomp-4.0/test/errors/b05-empty-input000066400000000000000000000002351514205130000200520ustar00rootroot00000000000000Test empty input with some leftover STDIN 31 32 33 34 35 STDERR [WARN]: 5 byte(s) at the end of input could not fit into a filter [WARN]: The input is empty ceccomp-4.0/test/errors/e01-asm-comparators000066400000000000000000000023701514205130000206700ustar00rootroot00000000000000Test comparators in asm STDIN if ($A == 0) goto end if ($A == 0) goto end, else goto kill if ($A != 0) goto end if ($A != 0) goto end, else goto kill if ($A > 0) goto end if ($A > 0) goto end, else goto kill if ($A <= 0) goto end if ($A <= 0) goto end, else goto kill if ($A >= 0) goto end if ($A >= 0) goto end, else goto kill if ($A < 0) goto end if ($A < 0) goto end, else goto kill if ($A & 0xff) goto end if ($A & 0xff) goto end, else goto kill if !($A & 0xff) goto end if !($A & 0xff) goto end, else goto kill return ERRNO(1) # separator end: return ALLOW kill: return KILL STDOUT "\x15\x00\x10\x00\x00\x00\x00\x00", "\x15\x00\x0f\x10\x00\x00\x00\x00", "\x15\x00\x00\x0e\x00\x00\x00\x00", "\x15\x00\x0e\x0d\x00\x00\x00\x00", "\x25\x00\x0c\x00\x00\x00\x00\x00", "\x25\x00\x0b\x0c\x00\x00\x00\x00", "\x25\x00\x00\x0a\x00\x00\x00\x00", "\x25\x00\x0a\x09\x00\x00\x00\x00", "\x35\x00\x08\x00\x00\x00\x00\x00", "\x35\x00\x07\x08\x00\x00\x00\x00", "\x35\x00\x00\x06\x00\x00\x00\x00", "\x35\x00\x06\x05\x00\x00\x00\x00", "\x45\x00\x04\x00\xff\x00\x00\x00", "\x45\x00\x03\x04\xff\x00\x00\x00", "\x45\x00\x00\x02\xff\x00\x00\x00", "\x45\x00\x02\x01\xff\x00\x00\x00", "\x06\x00\x00\x00\x01\x00\x05\x00", "\x06\x00\x00\x00\x00\x00\xff\x7f", "\x06\x00\x00\x00\x00\x00\x00\x00", ceccomp-4.0/test/errors/e02-disasm-comparators000066400000000000000000000034471514205130000213770ustar00rootroot00000000000000Test comparators in disasm STDIN 15 00 10 00 00 00 00 00 15 00 0f 10 00 00 00 00 15 00 00 0e 00 00 00 00 15 00 0e 0d 00 00 00 00 25 00 0c 00 00 00 00 00 25 00 0b 0c 00 00 00 00 25 00 00 0a 00 00 00 00 25 00 0a 09 00 00 00 00 35 00 08 00 00 00 00 00 35 00 07 08 00 00 00 00 35 00 00 06 00 00 00 00 35 00 06 05 00 00 00 00 45 00 04 00 ff 00 00 00 45 00 03 04 ff 00 00 00 45 00 00 02 ff 00 00 00 45 00 02 01 ff 00 00 00 06 00 00 00 01 00 05 00 06 00 00 00 00 00 ff 7f 06 00 00 00 00 00 00 00 STDOUT #Label CODE JT JF K #--------------------------------- L0001: 0x15 0x10 0x00 0x00000000 if ($A == 0x0) goto L0018 L0002: 0x15 0x0f 0x10 0x00000000 if ($A == 0x0) goto L0018, else goto L0019 L0003: 0x15 0x00 0x0e 0x00000000 if ($A != 0x0) goto L0018 L0004: 0x15 0x0e 0x0d 0x00000000 if ($A == 0x0) goto L0019, else goto L0018 L0005: 0x25 0x0c 0x00 0x00000000 if ($A > 0x0) goto L0018 L0006: 0x25 0x0b 0x0c 0x00000000 if ($A > 0x0) goto L0018, else goto L0019 L0007: 0x25 0x00 0x0a 0x00000000 if ($A <= 0x0) goto L0018 L0008: 0x25 0x0a 0x09 0x00000000 if ($A > 0x0) goto L0019, else goto L0018 L0009: 0x35 0x08 0x00 0x00000000 if ($A >= 0x0) goto L0018 L0010: 0x35 0x07 0x08 0x00000000 if ($A >= 0x0) goto L0018, else goto L0019 L0011: 0x35 0x00 0x06 0x00000000 if ($A < 0x0) goto L0018 L0012: 0x35 0x06 0x05 0x00000000 if ($A >= 0x0) goto L0019, else goto L0018 L0013: 0x45 0x04 0x00 0x000000ff if ($A & 0xff) goto L0018 L0014: 0x45 0x03 0x04 0x000000ff if ($A & 0xff) goto L0018, else goto L0019 L0015: 0x45 0x00 0x02 0x000000ff if !($A & 0xff) goto L0018 L0016: 0x45 0x02 0x01 0x000000ff if ($A & 0xff) goto L0019, else goto L0018 L0017: 0x06 0x00 0x00 0x00050001 return ERRNO(1) L0018: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0019: 0x06 0x00 0x00 0x00000000 return KILL #--------------------------------- ceccomp-4.0/test/shared_vars.py000066400000000000000000000030331514205130000166060ustar00rootroot00000000000000from pathlib import Path import subprocess import os from pytest import skip from types import SimpleNamespace PROJ_DIR = Path(__file__).parent.parent TEST_DIR = PROJ_DIR / 'test' TXT_DIR = TEST_DIR / 'text' BPF_DIR = TEST_DIR / 'bpf' EMU_DIR = TEST_DIR / 'emu_result' BE_DIR = TEST_DIR / 'big_endian_cases' ERR_CASE_DIR = TEST_DIR / 'errors' CECCOMP = str(PROJ_DIR / 'build' / 'ceccomp') FILENAMES = sorted([p.stem for p in TXT_DIR.iterdir()]) COMMON_OPTS = ['-c', 'always', '-a', 'x86_64'] def run_process( argv: list[str], is_binary: bool=False, extra_fd: int | None=None, stdin: str | bytes | None=None, ) -> tuple[int, str | bytes, str | bytes]: if extra_fd is None: result = subprocess.run(argv, timeout=3, capture_output=True, text=not is_binary, input=stdin) else: result = subprocess.run(argv, timeout=3, capture_output=True, text=not is_binary, pass_fds=(extra_fd, ), input=stdin) return result.returncode, result.stdout, result.stderr _, _verstr, _ = run_process(['pkg-config', '--modversion', 'libseccomp'], False) SKIP_CHROMIUM = tuple(_verstr.split('.')) < ('2', '5', '6') SKIP_REASON = 'libseccomp too old (<2.5.6)' def maybe_skip(filename: str): if SKIP_CHROMIUM and filename == 'chromium': skip(SKIP_REASON) def filter2text(filters: bytes) -> str: length = len(filters) # leftover (less than 8 bytes) will be discarded return '\n'.join(filters[i:i + 8].hex(' ') for i in range(0, length, 8)) os.environ['LC_ALL'] = 'C' ceccomp-4.0/test/test_asm.py000066400000000000000000000027041514205130000161300ustar00rootroot00000000000000import pytest from shared_vars import * @pytest.mark.parametrize('filename', FILENAMES) def test_asm(filename: str, errns: SimpleNamespace): maybe_skip(filename) input_file = TXT_DIR / filename expect_file = BPF_DIR / f'{filename}.bpf' _, stdout, stderr = run_process( [CECCOMP, 'asm', *COMMON_OPTS, '-f', 'raw', str(input_file)], True, ) errns.stderr = stderr.decode() with expect_file.open('rb') as expect: assert filter2text(stdout) == filter2text(expect.read()) def test_s390x_asm(errns: SimpleNamespace): input_file = BE_DIR / 's390x.text' expect_file = BE_DIR / 's390x.hexfmt' _, stdout, stderr = run_process( [CECCOMP, 'asm', '-f', 'hexfmt', str(input_file), '-a', 's390x'], ) errns.stderr = stderr with expect_file.open() as expect: assert stdout == expect.read() ERROR_IDS = sorted([p.stem[1:] for p in ERR_CASE_DIR.glob('a*')]) @pytest.mark.parametrize('errorid', ERROR_IDS) def test_error_cases(errorid: str): chunk_file = ERR_CASE_DIR / f'a{errorid}' with chunk_file.open() as f: blob = f.read() in_idx = blob.find('STDIN') err_idx = blob.find('STDERR') assert in_idx != -1 and err_idx != -1 stdin = blob[in_idx + 6 : err_idx] _, _, stderr = run_process( # error case for asm will not print to stdout, # so no need to specify arch [CECCOMP, 'asm', '-'], stdin=stdin, ) assert stderr == blob[err_idx + 7:] ceccomp-4.0/test/test_disasm.py000066400000000000000000000030451514205130000166270ustar00rootroot00000000000000import pytest from shared_vars import * @pytest.mark.parametrize('filename', FILENAMES) def test_disasm(filename: str, errns: SimpleNamespace): maybe_skip(filename) input_file = BPF_DIR / f'{filename}.bpf' expect_file = TXT_DIR / filename _, stdout, stderr = run_process( [CECCOMP, 'disasm', *COMMON_OPTS, str(input_file)], ) errns.stderr = stderr with expect_file.open('r') as expect: assert stdout == expect.read() def test_s390x_disasm(errns: SimpleNamespace): input_file = BE_DIR / 's390x.bpf' expect_file = BE_DIR / 's390x.disasm' _, stdout, stderr = run_process( [CECCOMP, 'disasm', str(input_file), '-a', 's390x'], ) errns.stderr = stderr with expect_file.open() as expect: assert stdout == expect.read() ERROR_IDS = sorted([p.stem[1:] for p in ERR_CASE_DIR.glob('b*')]) @pytest.mark.parametrize('errorid', ERROR_IDS) def test_error_cases(errorid: str): chunk_file = ERR_CASE_DIR / f'b{errorid}' with chunk_file.open() as f: blob = f.read() in_idx = blob.find('STDIN') out_idx = blob.find('STDOUT') err_idx = blob.find('STDERR') assert in_idx != -1 and err_idx != -1 stdin = bytes.fromhex(blob[in_idx + 6 : err_idx]) _, stdout, stderr = run_process( [CECCOMP, 'disasm', '-', '-a', 'x86_64'], stdin=stdin, is_binary=True, ) if out_idx == -1: assert stderr.decode() == blob[err_idx + 7:] else: assert stderr.decode() == blob[err_idx + 7 : out_idx] assert stdout.decode() == blob[out_idx + 7:] ceccomp-4.0/test/test_dynamic.py000066400000000000000000000110111514205130000167630ustar00rootroot00000000000000import pytest from shared_vars import * from subprocess import PIPE, DEVNULL import signal import time import select def is_not_cap_sys_admin() -> str | None: try: with open('/proc/self/status') as f: for line in f: if line.startswith('CapEff:'): capeff = int(line.split()[1], 16) break else: return 'Capability can not be found in status' except OSError: return 'Can not query /proc to know capability' if bool(capeff & (1 << 21)): # CAP_SYS_ADMIN = 21 return None return 'Lack of CAP_SYS_ADMIN capability' TEST = str(PROJ_DIR / 'build' / 'test') assert run_process(['make', '-C', str(PROJ_DIR), 'test'], False)[0] == 0 def pid_state(pid: int) -> str | None: """ Race condition: perhaps kernel killed process but ceccomp hasn't exit, so test is zombie and not being collected. Test this case """ try: os.kill(pid, 0) except ProcessLookupError: return None try: with open(f'/proc/{pid}/stat') as f: state = f.read().split(' ', 4)[2] except: return None else: return None if state == 'Z' else state def filter_execve_k(text: str) -> str: if len(text) != 391: return text try: int(text[174:184], 16) except ValueError: return text return text[:174] + ' MAY VARY ' + text[184:] # -a x86_64 option in COMMON_OPTS will be ignored in trace/probe ##### TEST CASES ##### def test_probe(errns: SimpleNamespace): piper, pipew = os.pipe() os.set_inheritable(pipew, True) argv = [CECCOMP, 'probe', *COMMON_OPTS, '-o', f'/proc/self/fd/{pipew}', TEST, '1'] _, stdout, stderr = run_process(argv, False, pipew) os.close(pipew) errns.stderr = stderr expect_file = TEST_DIR / 'dyn_log' / 'probe.log' with expect_file.open() as f: expect = f.read() with os.fdopen(piper) as f: assert f.read() == expect pid = int(stdout.split('=')[1]) end = time.time() + 1 while pid_state(pid) and time.time() < end: time.sleep(0.0625) assert pid_state(pid) is None def test_trace(errns: SimpleNamespace): piper, pipew = os.pipe() os.set_inheritable(pipew, True) argv = [CECCOMP, 'trace', *COMMON_OPTS, '-o', f'/proc/self/fd/{pipew}', TEST, '0'] _, _, stderr = run_process(argv, False, pipew) os.close(pipew) errns.stderr = stderr expect_file = TEST_DIR / 'dyn_log' / 'trace.log' with expect_file.open() as f: expect = f.read() with os.fdopen(piper) as f: assert filter_execve_k(f.read()) == filter_execve_k(expect) assert 'WARN' in stderr def test_seize(errns: SimpleNamespace): tp = subprocess.Popen([TEST, '2'], stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL, text=True) pid = int(tp.stdout.readline().split('=')[1]) argv = [CECCOMP, 'trace', *COMMON_OPTS, '-p', str(pid), '-s'] cp = subprocess.Popen(argv, stdin=DEVNULL, stdout=PIPE, stderr=PIPE, text=True) pre_line = cp.stderr.readline() os.kill(pid, signal.SIGCONT) rl, _, _ = select.select([tp.stdout], [], [], 0.5) if rl: pid = int(rl[0].readline().split('=')[1]) # child pid else: with open(f'/proc/{tp.pid}/wchan') as f: t_kfunc = f.read() with open(f'/proc/{cp.pid}/wchan') as f: c_kfunc = f.read() errns.stderr = f'TEST in {t_kfunc}\nCECCOMP in {c_kfunc}' tp.terminate() cp.terminate() assert False, 'Found signal race condition? Pls report to upstream' cp.terminate() stdout, stderr = cp.communicate() errns.stderr = pre_line + stderr pid_exist = True try: os.kill(pid, 0) except ProcessLookupError: pid_exist = False else: os.kill(pid, signal.SIGCONT) assert pid_exist is True expect_file = TEST_DIR / 'dyn_log' / 'trace.log' with expect_file.open() as f: assert filter_execve_k(stdout) == filter_execve_k(f.read()) def test_trace_pid(errns: SimpleNamespace): if msg := is_not_cap_sys_admin(): pytest.skip(msg) tp = subprocess.Popen([TEST, '3'], stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL, text=True) pid = int(tp.stdout.readline().split('=')[1]) _, stdout, stderr = run_process( [CECCOMP, 'trace', *COMMON_OPTS, '-p', str(pid)], ) errns.stderr = stderr os.kill(pid, signal.SIGCONT) expect_file = TEST_DIR / 'dyn_log' / 'trace.log' with expect_file.open() as f: assert filter_execve_k(stdout) == filter_execve_k(f.read()) ceccomp-4.0/test/test_edge_case.py000066400000000000000000000055711514205130000172540ustar00rootroot00000000000000import pytest from shared_vars import * def test_asm_large_file(): _, _, stderr = run_process( [CECCOMP, 'asm', '/dev/zero'], ) assert stderr == '[ERROR]: The input file is greater than 1 MiB!\n' def test_asm_no_lf(): _, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='1' * 0x400, ) assert stderr == "[ERROR]: No line break in source file, perhaps it's not a text file?\n" def test_asm_long_line(): _, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='\n\n' + '1' * 0x400 + '\n', ) assert stderr == '[ERROR]: Line 3 has more than 384 bytes, perhaps the input is not a text file?\n' def test_asm_many_lines(): _, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='\n' * 0x2000, ) assert stderr == "[ERROR]: Found more than 4096 lines of text, perhaps it's not for ceccomp?\n" def test_asm_0_file(): _, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='\n\n\0\n', ) assert stderr == "[ERROR]: Found '\\0' at file offset 2, perhaps it's not a text file?\n" def test_asm_empty_file(): _, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='# \n', ) assert stderr == '[ERROR]: The input does not contain any valid statement\n' def test_asm_4096_statements(errns: SimpleNamespace): exit_code, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='return ALLOW\n\n' * 1024, ) errns.stderr = stderr assert exit_code == 0 def test_asm_4097_statements(): _, _, stderr = run_process( [CECCOMP, 'asm', '-'], stdin='return ALLOW\n' * 1025, ) assert stderr == '[ERROR]: Input file has more than 1024 statements!\n' def test_disasm_large_file(): _, _, stderr = run_process( [CECCOMP, 'disasm', '/dev/zero'], ) assert stderr == '[ERROR]: The input is larger than 1024 filters! Perhaps inputting a wrong file?\n' def test_asm_comparators(errns: SimpleNamespace): chunk_file = ERR_CASE_DIR / 'e01-asm-comparators' with chunk_file.open() as f: blob = f.read() in_idx = blob.find('STDIN') out_idx = blob.find('STDOUT') assert in_idx != -1 and out_idx != -1 stdin = blob[in_idx + 6 : out_idx] _, stdout, stderr = run_process( [CECCOMP, 'asm', '-', '-a', 'x86_64', '-f', 'hexfmt'], stdin=stdin, ) errns.stderr = stderr assert stdout == blob[out_idx + 7:] def test_disasm_comparators(errns: SimpleNamespace): chunk_file = ERR_CASE_DIR / 'e02-disasm-comparators' with chunk_file.open() as f: blob = f.read() in_idx = blob.find('STDIN') out_idx = blob.find('STDOUT') assert in_idx != -1 and out_idx != -1 stdin = bytes.fromhex(blob[in_idx + 6 : out_idx]) _, stdout, stderr = run_process( [CECCOMP, 'disasm', '-', '-a', 'x86_64'], stdin=stdin, is_binary=True, ) errns.stderr = stderr assert stdout.decode() == blob[out_idx + 7:] ceccomp-4.0/test/test_emu.py000066400000000000000000000040551514205130000161370ustar00rootroot00000000000000import pytest from shared_vars import * EMU_TARGETS = [] for filename in FILENAMES: EMU_TARGETS.append((filename, 'accept')) EMU_TARGETS.append((filename, 'open')) EMU_TARGETS.append((filename, 'pipe')) @pytest.mark.parametrize('filename, suffix', EMU_TARGETS) def test_emu(filename: str, suffix: str, errns: SimpleNamespace): maybe_skip(filename) input_file = TXT_DIR / filename expect_file = EMU_DIR / f'{filename}.{suffix}' emu_args = [suffix, '1', '2', '3', '4', '5', '6'] _, stdout, stderr = run_process( [CECCOMP, 'emu', *COMMON_OPTS, str(input_file), *emu_args], ) errns.stderr = stderr with expect_file.open('r') as expect: assert stdout == expect.read() def test_s390x_emu(errns: SimpleNamespace): input_file = BE_DIR / 's390x.text' expect_file = BE_DIR / 's390x.text.mmap' _, stdout, stderr = run_process( [CECCOMP, 'emu', str(input_file), 'mmap', '-a', 's390x'], ) errns.stderr = stderr with expect_file.open() as expect: assert stdout == expect.read() def test_return_A(errns: SimpleNamespace): input_str = '$A = 0x50005\nreturn $A\n' _, stdout, stderr = run_process( [CECCOMP, 'emu', '-', '1', '-q'], stdin=input_str, ) errns.stderr = stderr assert stdout == 'ERRNO(5)\n' def test_return_A_long(errns: SimpleNamespace): input_str = 'return $A\n' _, stdout, stderr = run_process( [CECCOMP, 'emu', '-', '1'], stdin=input_str, ) errns.stderr = stderr assert stdout == 'return $A # A = 0, KILL\n' def test_return_number(errns: SimpleNamespace): input_str = 'return 0x13371337\n' _, stdout, stderr = run_process( [CECCOMP, 'emu', '-', '1', '-q'], stdin=input_str, ) errns.stderr = stderr assert stdout == 'KILL_PROCESS\n' def test_return_number_long(errns: SimpleNamespace): input_str = 'return 0x7ff01000\n' _, stdout, stderr = run_process( [CECCOMP, 'emu', '-', '1'], stdin=input_str, ) errns.stderr = stderr assert stdout == 'return 0x7ff01000 # TRACE(4096)\n' ceccomp-4.0/test/text/000077500000000000000000000000001514205130000147205ustar00rootroot00000000000000ceccomp-4.0/test/text/CONFidence-2017-amigo000066400000000000000000000151161514205130000202250ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x01 0x40000003 if ($A != i386) goto L0004 L0003: 0x05 0x00 0x00 0x0000000a goto L0014 L0004: 0x20 0x00 0x00 0x00000038 $A = $low_args[5] L0005: 0x02 0x00 0x00 0x00000000 $mem[0x0] = $A L0006: 0x20 0x00 0x00 0x0000003c $A = $high_args[5] L0007: 0x02 0x00 0x00 0x00000001 $mem[0x1] = $A L0008: 0x15 0x00 0x03 0x03133731 if ($A != 0x3133731) goto L0012 L0009: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0010: 0x15 0x02 0x00 0x33731337 if ($A == 0x33731337) goto L0013 L0011: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0012: 0x06 0x00 0x00 0x00000000 return KILL L0013: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0014: 0x05 0x00 0x00 0x00000000 goto L0015 L0015: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0016: 0x15 0x00 0x01 0x000003e7 if ($A != 0x3e7) goto L0018 L0017: 0x06 0x00 0x00 0x0005053b return ERRNO(1339) L0018: 0x15 0x00 0x01 0x00000004 if ($A != 0x4) goto L0020 L0019: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0020: 0x15 0x00 0x01 0x00000092 if ($A != 0x92) goto L0022 L0021: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0022: 0x15 0x00 0x01 0x00000003 if ($A != 0x3) goto L0024 L0023: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0024: 0x15 0x00 0x01 0x000000c5 if ($A != 0xc5) goto L0026 L0025: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0026: 0x15 0x00 0x01 0x0000008c if ($A != 0x8c) goto L0028 L0027: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0028: 0x15 0x00 0x01 0x000000fc if ($A != 0xfc) goto L0030 L0029: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0030: 0x15 0x00 0x01 0x000000c0 if ($A != 0xc0) goto L0032 L0031: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0032: 0x15 0x00 0x01 0x000000af if ($A != 0xaf) goto L0034 L0033: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0034: 0x15 0x00 0x01 0x000000ae if ($A != 0xae) goto L0036 L0035: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0036: 0x15 0x00 0x01 0x0000002d if ($A != 0x2d) goto L0038 L0037: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0038: 0x15 0x00 0x01 0x00000025 if ($A != 0x25) goto L0040 L0039: 0x05 0x00 0x00 0x0000000f goto L0055 L0040: 0x15 0x00 0x01 0x00000101 if ($A != 0x101) goto L0042 L0041: 0x05 0x00 0x00 0x00000023 goto L0077 L0042: 0x15 0x00 0x01 0x0000010e if ($A != 0x10e) goto L0044 L0043: 0x06 0x00 0x00 0x00050000 return ERRNO(0) L0044: 0x15 0x00 0x01 0x00000005 if ($A != 0x5) goto L0046 L0045: 0x06 0x00 0x00 0x0005007e return ERRNO(126) L0046: 0x15 0x00 0x01 0x00000088 if ($A != 0x88) goto L0048 L0047: 0x06 0x00 0x00 0x0005007e return ERRNO(126) L0048: 0x15 0x00 0x01 0x00000014 if ($A != 0x14) goto L0050 L0049: 0x06 0x00 0x00 0x00050539 return ERRNO(1337) L0050: 0x15 0x00 0x01 0x000000e0 if ($A != 0xe0) goto L0052 L0051: 0x06 0x00 0x00 0x00050539 return ERRNO(1337) L0052: 0x15 0x00 0x01 0x00000038 if ($A != 0x38) goto L0054 L0053: 0x06 0x00 0x00 0x7ff00000 return TRACE(0) L0054: 0x06 0x00 0x00 0x00000000 return KILL L0055: 0x05 0x00 0x00 0x00000000 goto L0056 L0056: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0057: 0x02 0x00 0x00 0x00000000 $mem[0x0] = $A L0058: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0059: 0x02 0x00 0x00 0x00000001 $mem[0x1] = $A L0060: 0x15 0x00 0x03 0x00000000 if ($A != 0x0) goto L0064 L0061: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0062: 0x15 0x02 0x00 0x00001d93 if ($A == 0x1d93) goto L0065 L0063: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0064: 0x06 0x00 0x00 0x00000000 return KILL L0065: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0066: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0067: 0x02 0x00 0x00 0x00000000 $mem[0x0] = $A L0068: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0069: 0x02 0x00 0x00 0x00000001 $mem[0x1] = $A L0070: 0x15 0x00 0x03 0x00000000 if ($A != 0x0) goto L0074 L0071: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0072: 0x15 0x02 0x00 0x00000013 if ($A == 0x13) goto L0075 L0073: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0074: 0x06 0x00 0x00 0x00000000 return KILL L0075: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0076: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0077: 0x05 0x00 0x00 0x00000000 goto L0078 L0078: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0079: 0x02 0x00 0x00 0x00000000 $mem[0x0] = $A L0080: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0081: 0x02 0x00 0x00 0x00000001 $mem[0x1] = $A L0082: 0x15 0x00 0x03 0xffffffff if ($A != 0xffffffff) goto L0086 L0083: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0084: 0x15 0x02 0x00 0xffffff9c if ($A == 0xffffff9c) goto L0087 L0085: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0086: 0x06 0x00 0x00 0x00000000 return KILL L0087: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0088: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0089: 0x02 0x00 0x00 0x00000000 $mem[0x0] = $A L0090: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0091: 0x02 0x00 0x00 0x00000001 $mem[0x1] = $A L0092: 0x15 0x00 0x03 0x12345678 if ($A != 0x12345678) goto L0096 L0093: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0094: 0x15 0x02 0x00 0x00000000 if ($A == 0x0) goto L0097 L0095: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0096: 0x06 0x00 0x00 0x00000000 return KILL L0097: 0x60 0x00 0x00 0x00000001 $A = $mem[0x1] L0098: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- ceccomp-4.0/test/text/DEF-CON-2020-bdooos000066400000000000000000000032341514205130000175240ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x10 0xc00000b7 if ($A != aarch64) goto L0019 L0003: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0004: 0x15 0x0d 0x00 0x0000001d if ($A == aarch64.ioctl) goto L0018 L0005: 0x15 0x0c 0x00 0x0000003f if ($A == aarch64.read) goto L0018 L0006: 0x15 0x0b 0x00 0x00000040 if ($A == aarch64.write) goto L0018 L0007: 0x15 0x0a 0x00 0x00000049 if ($A == aarch64.ppoll) goto L0018 L0008: 0x15 0x09 0x00 0x0000005e if ($A == aarch64.exit_group) goto L0018 L0009: 0x15 0x08 0x00 0x00000062 if ($A == aarch64.futex) goto L0018 L0010: 0x15 0x07 0x00 0x00000084 if ($A == aarch64.sigaltstack) goto L0018 L0011: 0x15 0x06 0x00 0x00000086 if ($A == aarch64.rt_sigaction) goto L0018 L0012: 0x15 0x05 0x00 0x0000008b if ($A == aarch64.rt_sigreturn) goto L0018 L0013: 0x15 0x04 0x00 0x000000ce if ($A == aarch64.sendto) goto L0018 L0014: 0x15 0x03 0x00 0x000000cf if ($A == aarch64.recvfrom) goto L0018 L0015: 0x15 0x02 0x00 0x000000d0 if ($A == aarch64.setsockopt) goto L0018 L0016: 0x15 0x01 0x00 0x000000d7 if ($A == aarch64.munmap) goto L0018 L0017: 0x06 0x00 0x00 0x80000000 return KILL_PROCESS L0018: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0019: 0x06 0x00 0x00 0x00000000 return KILL #--------------------------------- ceccomp-4.0/test/text/chromium000066400000000000000000001351001514205130000164660ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x01 0x00 0xc000003e if ($A == x86_64) goto L0004 L0003: 0x06 0x00 0x00 0x0003000a return TRAP(10) L0004: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0005: 0x45 0x00 0x01 0x40000000 if !($A & 0x40000000) goto L0007 L0006: 0x06 0x00 0x00 0x00030009 return TRAP(9) L0007: 0x25 0x00 0x4a 0x00000092 if ($A <= sched_get_priority_max) goto L0082 L0008: 0x25 0x00 0x24 0x0000010e if ($A <= pselect6) goto L0045 L0009: 0x25 0x00 0x12 0x0000012f if ($A <= name_to_handle_at) goto L0028 L0010: 0x25 0x00 0x09 0x0000014d if ($A <= io_pgetevents) goto L0020 L0011: 0x25 0x00 0x05 0x000001b4 if ($A <= close_range) goto L0017 L0012: 0x25 0x00 0x03 0x000001b8 if ($A <= process_madvise) goto L0016 L0013: 0x25 0x00 0x01 0x000001ce if ($A <= mseal) goto L0015 L0014: 0x25 0x00 0x42 0x000001cf if ($A <= setxattrat) goto L0081 L0015: 0x05 0x00 0x00 0x0000024c goto L0604 L0016: 0x35 0x9a 0xff 0x000001b7 if ($A >= faccessat2) goto L0171, else goto L0272 L0017: 0x25 0x00 0x01 0x0000014f if ($A <= uretprobe) goto L0019 L0018: 0x35 0x99 0xfd 0x000001b2 if ($A >= pidfd_open) goto L0172, else goto L0272 L0019: 0x35 0x3d 0xfc 0x0000014e if ($A >= rseq) goto L0081, else goto L0272 L0020: 0x25 0x00 0x04 0x00000140 if ($A <= kexec_file_load) goto L0025 L0021: 0x25 0x00 0x02 0x0000014a if ($A <= pkey_alloc) goto L0024 L0022: 0x25 0x00 0x96 0x0000014b if ($A <= pkey_free) goto L0173 L0023: 0x35 0x8c 0x39 0x0000014c if ($A >= statx) goto L0164, else goto L0081 L0024: 0x35 0x86 0xf7 0x00000149 if ($A >= pkey_mprotect) goto L0159, else goto L0272 L0025: 0x25 0x00 0x01 0x0000013e if ($A <= getrandom) goto L0027 L0026: 0x35 0x36 0x99 0x0000013f if ($A >= memfd_create) goto L0081, else goto L0180 L0027: 0x35 0x8f 0xf4 0x0000013c if ($A >= renameat2) goto L0171, else goto L0272 L0028: 0x25 0x00 0x08 0x0000011e if ($A <= timerfd_settime) goto L0037 L0029: 0x25 0x00 0x04 0x00000125 if ($A <= pipe2) goto L0034 L0030: 0x25 0x00 0x02 0x00000129 if ($A <= rt_tgsigqueueinfo) goto L0033 L0031: 0x25 0x00 0xa2 0x0000012a if ($A <= perf_event_open) goto L0194 L0032: 0x35 0x9a 0xef 0x0000012e if ($A >= prlimit64) goto L0187, else goto L0272 L0033: 0x35 0xee 0xa7 0x00000126 if ($A >= inotify_init1) goto L0272, else goto L0201 L0034: 0x25 0x00 0x01 0x00000121 if ($A <= signalfd4) goto L0036 L0035: 0x35 0x2d 0xec 0x00000122 if ($A >= eventfd2) goto L0081, else goto L0272 L0036: 0x35 0x86 0xeb 0x00000120 if ($A >= accept4) goto L0171, else goto L0272 L0037: 0x25 0x00 0x04 0x00000118 if ($A <= utimensat) goto L0042 L0038: 0x25 0x00 0x02 0x0000011a if ($A <= signalfd) goto L0041 L0039: 0x25 0x00 0xe8 0x0000011c if ($A <= eventfd) goto L0272 L0040: 0x35 0x82 0x28 0x0000011d if ($A >= fallocate) goto L0171, else goto L0081 L0041: 0x35 0x27 0x81 0x00000119 if ($A >= epoll_pwait) goto L0081, else goto L0171 L0042: 0x25 0x00 0x01 0x00000111 if ($A <= set_robust_list) goto L0044 L0043: 0x35 0xe4 0x25 0x00000112 if ($A >= get_robust_list) goto L0272, else goto L0081 L0044: 0x35 0xe3 0x24 0x00000110 if ($A >= unshare) goto L0272, else goto L0081 L0045: 0x25 0x00 0x11 0x000000d4 if ($A <= lookup_dcookie) goto L0063 L0046: 0x25 0x00 0x08 0x000000e7 if ($A <= exit_group) goto L0055 L0047: 0x25 0x00 0x04 0x000000f7 if ($A <= waitid) goto L0052 L0048: 0x25 0x00 0x02 0x00000101 if ($A <= openat) goto L0051 L0049: 0x25 0x00 0x79 0x00000106 if ($A <= newfstatat) goto L0171 L0050: 0x35 0x78 0x9d 0x00000107 if ($A >= unlinkat) goto L0171, else goto L0208 L0051: 0x35 0xdc 0x1d 0x000000f8 if ($A >= add_key) goto L0272, else goto L0081 L0052: 0x25 0x00 0x01 0x000000eb if ($A <= utimes) goto L0054 L0053: 0x35 0xda 0x75 0x000000ec if ($A >= vserver) goto L0272, else goto L0171 L0054: 0x35 0x4b 0x1a 0x000000ea if ($A >= tgkill) goto L0130, else goto L0081 L0055: 0x25 0x00 0x04 0x000000da if ($A <= set_tid_address) goto L0060 L0056: 0x25 0x00 0x02 0x000000dc if ($A <= semtimedop) goto L0059 L0057: 0x25 0x00 0x71 0x000000dd if ($A <= fadvise64) goto L0171 L0058: 0x35 0x96 0xd5 0x000000e4 if ($A >= clock_gettime) goto L0209, else goto L0272 L0059: 0x35 0x15 0xd4 0x000000db if ($A >= restart_syscall) goto L0081, else goto L0272 L0060: 0x25 0x00 0x01 0x000000d6 if ($A <= epoll_ctl_old) goto L0062 L0061: 0x35 0x6d 0xd2 0x000000d9 if ($A >= getdents64) goto L0171, else goto L0272 L0062: 0x35 0x12 0x6c 0x000000d5 if ($A >= epoll_create) goto L0081, else goto L0171 L0063: 0x25 0x00 0x09 0x000000ae if ($A <= create_module) goto L0073 L0064: 0x25 0x00 0x04 0x000000c9 if ($A <= time) goto L0069 L0065: 0x25 0x00 0x02 0x000000cb if ($A <= sched_setaffinity) goto L0068 L0066: 0x25 0x00 0xcd 0x000000cc if ($A <= sched_getaffinity) goto L0272 L0067: 0x35 0xcc 0x16 0x000000cd if ($A >= set_thread_area) goto L0272, else goto L0090 L0068: 0x35 0xcc 0x0c 0x000000ca if ($A >= futex) goto L0273, else goto L0081 L0069: 0x25 0x00 0x02 0x000000bb if ($A <= readahead) goto L0072 L0070: 0x25 0x00 0xc9 0x000000c8 if ($A <= tkill) goto L0272 L0071: 0x05 0x00 0x00 0x000001b3 goto L0507 L0072: 0x35 0x08 0xc7 0x000000ba if ($A >= gettid) goto L0081, else goto L0272 L0073: 0x25 0x00 0x04 0x0000009d if ($A <= prctl) goto L0078 L0074: 0x25 0x00 0x02 0x000000a0 if ($A <= setrlimit) goto L0077 L0075: 0x25 0x00 0x05 0x000000a1 if ($A <= chroot) goto L0081 L0076: 0x35 0x5e 0xc3 0x000000ac if ($A >= iopl) goto L0171, else goto L0272 L0077: 0x35 0xc2 0xfb 0x0000009e if ($A >= arch_prctl) goto L0272, else goto L0329 L0078: 0x25 0x00 0x01 0x00000095 if ($A <= mlock) goto L0080 L0079: 0x35 0xc0 0x01 0x00000097 if ($A >= mlockall) goto L0272, else goto L0081 L0080: 0x35 0xbf 0x00 0x00000094 if ($A >= sched_rr_get_interval) goto L0272 L0081: 0x05 0x00 0x00 0x00000209 goto L0603 L0082: 0x25 0x00 0x25 0x0000004c if ($A <= truncate) goto L0120 L0083: 0x25 0x00 0x13 0x00000076 if ($A <= getresuid) goto L0103 L0084: 0x25 0x00 0x0a 0x00000081 if ($A <= rt_sigqueueinfo) goto L0095 L0085: 0x25 0x00 0x06 0x00000088 if ($A <= ustat) goto L0092 L0086: 0x25 0x00 0x04 0x0000008c if ($A <= getpriority) goto L0091 L0087: 0x35 0x01 0x00 0x0000008e if ($A >= sched_setparam) goto L0089 L0088: 0x05 0x00 0x00 0x00000133 goto L0396 L0089: 0x25 0x00 0xb6 0x0000008f if ($A <= sched_getparam) goto L0272 L0090: 0x05 0x00 0x00 0x00000122 goto L0381 L0091: 0x35 0xb4 0x4f 0x0000008a if ($A >= fstatfs) goto L0272, else goto L0171 L0092: 0x25 0x00 0x01 0x00000084 if ($A <= utime) goto L0094 L0093: 0x35 0xb2 0x4d 0x00000087 if ($A >= personality) goto L0272, else goto L0171 L0094: 0x35 0xf9 0xb1 0x00000083 if ($A >= sigaltstack) goto L0344, else goto L0272 L0095: 0x25 0x00 0x04 0x0000007a if ($A <= setfsuid) goto L0100 L0096: 0x25 0x00 0x02 0x0000007e if ($A <= capset) goto L0099 L0097: 0x25 0x00 0x49 0x0000007f if ($A <= rt_sigpending) goto L0171 L0098: 0x35 0xf5 0xad 0x00000080 if ($A >= rt_sigtimedwait) goto L0344, else goto L0272 L0099: 0x35 0xf4 0x47 0x0000007c if ($A >= getsid) goto L0344, else goto L0171 L0100: 0x25 0x00 0x01 0x00000078 if ($A <= getresgid) goto L0102 L0101: 0x35 0xaa 0xf2 0x00000079 if ($A >= getpgid) goto L0272, else goto L0344 L0102: 0x35 0x44 0xf1 0x00000077 if ($A >= setresgid) goto L0171, else goto L0344 L0103: 0x25 0x00 0x08 0x00000068 if ($A <= getgid) goto L0112 L0104: 0x25 0x00 0x04 0x0000006e if ($A <= getppid) goto L0109 L0105: 0x25 0x00 0x02 0x00000071 if ($A <= setreuid) goto L0108 L0106: 0x25 0x00 0x40 0x00000073 if ($A <= getgroups) goto L0171 L0107: 0x35 0x3f 0xec 0x00000074 if ($A >= setgroups) goto L0171, else goto L0344 L0108: 0x35 0xa3 0xeb 0x0000006f if ($A >= getpgrp) goto L0272, else goto L0344 L0109: 0x25 0x00 0x01 0x0000006b if ($A <= geteuid) goto L0111 L0110: 0x35 0xa1 0xe9 0x0000006d if ($A >= setpgid) goto L0272, else goto L0344 L0111: 0x35 0x3b 0xe8 0x00000069 if ($A >= setuid) goto L0171, else goto L0344 L0112: 0x25 0x00 0x04 0x00000062 if ($A <= getrusage) goto L0117 L0113: 0x25 0x00 0x02 0x00000065 if ($A <= ptrace) goto L0116 L0114: 0x25 0x00 0x9d 0x00000066 if ($A <= getuid) goto L0272 L0115: 0x35 0x9c 0xe4 0x00000067 if ($A >= syslog) goto L0272, else goto L0344 L0116: 0x35 0xe3 0x9b 0x00000063 if ($A >= sysinfo) goto L0344, else goto L0272 L0117: 0x25 0x00 0x01 0x0000004e if ($A <= getdents) goto L0119 L0118: 0x35 0xe1 0x34 0x00000060 if ($A >= gettimeofday) goto L0344, else goto L0171 L0119: 0x35 0xe0 0x33 0x0000004d if ($A >= ftruncate) goto L0344, else goto L0171 L0120: 0x25 0x00 0x16 0x00000020 if ($A <= dup) goto L0143 L0121: 0x25 0x00 0x0b 0x00000038 if ($A <= clone) goto L0133 L0122: 0x25 0x00 0x05 0x0000003f if ($A <= uname) goto L0128 L0123: 0x25 0x00 0x03 0x00000048 if ($A <= fcntl) goto L0127 L0124: 0x35 0x01 0x00 0x00000049 if ($A >= flock) goto L0126 L0125: 0x05 0x00 0x00 0x00000123 goto L0417 L0126: 0x35 0xd9 0x91 0x0000004a if ($A >= fsync) goto L0344, else goto L0272 L0127: 0x35 0x2b 0xd8 0x00000040 if ($A >= semget) goto L0171, else goto L0344 L0128: 0x25 0x00 0x02 0x0000003c if ($A <= exit) goto L0131 L0129: 0x25 0x00 0xd6 0x0000003e if ($A <= kill) goto L0344 L0130: 0x05 0x00 0x00 0x00000171 goto L0500 L0131: 0x35 0x27 0x00 0x00000039 if ($A >= fork) goto L0171 L0132: 0x05 0x00 0x00 0x00000177 goto L0508 L0133: 0x25 0x00 0x06 0x0000002c if ($A <= sendto) goto L0140 L0134: 0x25 0x00 0x04 0x00000033 if ($A <= getsockname) goto L0139 L0135: 0x25 0x00 0x88 0x00000035 if ($A <= socketpair) goto L0272 L0136: 0x35 0x01 0x00 0x00000036 if ($A >= setsockopt) goto L0138 L0137: 0x05 0x00 0x00 0x0000018a goto L0532 L0138: 0x05 0x00 0x00 0x0000017b goto L0518 L0139: 0x35 0x1f 0xcc 0x00000031 if ($A >= bind) goto L0171, else goto L0344 L0140: 0x25 0x00 0x01 0x00000027 if ($A <= getpid) goto L0142 L0141: 0x35 0x1d 0xca 0x00000028 if ($A >= sendfile) goto L0171, else goto L0344 L0142: 0x35 0x81 0xc9 0x00000024 if ($A >= getitimer) goto L0272, else goto L0344 L0143: 0x25 0x00 0x0a 0x0000000b if ($A <= munmap) goto L0154 L0144: 0x25 0x00 0x05 0x00000016 if ($A <= pipe) goto L0150 L0145: 0x25 0x00 0x03 0x0000001b if ($A <= mincore) goto L0149 L0146: 0x25 0x00 0xc5 0x0000001c if ($A <= madvise) goto L0344 L0147: 0x35 0x17 0x00 0x0000001d if ($A >= shmget) goto L0171 L0148: 0x05 0x00 0x00 0x00000186 goto L0539 L0149: 0x35 0x7a 0xc2 0x0000001a if ($A >= msync) goto L0272, else goto L0344 L0150: 0x25 0x00 0x01 0x00000011 if ($A <= pread64) goto L0152 L0151: 0x35 0x13 0xc0 0x00000015 if ($A >= access) goto L0171, else goto L0344 L0152: 0x25 0x00 0xbf 0x00000010 if ($A <= ioctl) goto L0344 L0153: 0x05 0x00 0x00 0x000001a4 goto L0574 L0154: 0x25 0x00 0x06 0x00000005 if ($A <= fstat) goto L0161 L0155: 0x25 0x00 0x04 0x00000007 if ($A <= poll) goto L0160 L0156: 0x25 0x00 0xbb 0x00000009 if ($A <= mmap) goto L0344 L0157: 0x35 0x01 0x00 0x0000000a if ($A >= mprotect) goto L0159 L0158: 0x05 0x00 0x00 0x000001b3 goto L0594 L0159: 0x05 0x00 0x00 0x000001ab goto L0587 L0160: 0x35 0x0a 0xb7 0x00000006 if ($A >= lstat) goto L0171, else goto L0344 L0161: 0x25 0x00 0x01 0x00000003 if ($A <= close) goto L0163 L0162: 0x35 0x08 0xb5 0x00000004 if ($A >= stat) goto L0171, else goto L0344 L0163: 0x35 0x07 0xb4 0x00000002 if ($A >= open) goto L0171, else goto L0344 L0164: 0x20 0x00 0x00 0x0000002c $A = $high_args[3] L0165: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0169 L0166: 0x15 0x00 0xae 0xffffffff if ($A != 0xffffffff) goto L0341 L0167: 0x20 0x00 0x00 0x00000028 $A = $low_args[3] L0168: 0x45 0x00 0xac 0x80000000 if !($A & 0x80000000) goto L0341 L0169: 0x20 0x00 0x00 0x00000028 $A = $low_args[3] L0170: 0x15 0x01 0x00 0x000007ff if ($A == 0x7ff) goto L0172 L0171: 0x05 0x00 0x00 0x000001ae goto L0602 L0172: 0x06 0x00 0x00 0x00050026 return ERRNO(38) L0173: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0174: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0178 L0175: 0x15 0x00 0xa5 0xffffffff if ($A != 0xffffffff) goto L0341 L0176: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0177: 0x45 0x00 0xa3 0x80000000 if !($A & 0x80000000) goto L0341 L0178: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0179: 0x15 0xa4 0x5c 0x00000000 if ($A == 0x0) goto L0344, else goto L0272 L0180: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0181: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0185 L0182: 0x15 0x00 0x9e 0xffffffff if ($A != 0xffffffff) goto L0341 L0183: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0184: 0x45 0x00 0x9c 0x80000000 if !($A & 0x80000000) goto L0341 L0185: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0186: 0x45 0x55 0x9d 0xfffffffa if ($A & 0xfffffffa) goto L0272, else goto L0344 L0187: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0188: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0192 L0189: 0x15 0x00 0x97 0xffffffff if ($A != 0xffffffff) goto L0341 L0190: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0191: 0x45 0x00 0x95 0x80000000 if !($A & 0x80000000) goto L0341 L0192: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0193: 0x15 0x96 0x00 0x00000000 if ($A == 0x0) goto L0344 L0194: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0195: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0199 L0196: 0x15 0x00 0x90 0xffffffff if ($A != 0xffffffff) goto L0341 L0197: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0198: 0x45 0x00 0x8e 0x80000000 if !($A & 0x80000000) goto L0341 L0199: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0200: 0x05 0x00 0x00 0x000000d7 goto L0416 L0201: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0202: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0206 L0203: 0x15 0x00 0x89 0xffffffff if ($A != 0xffffffff) goto L0341 L0204: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0205: 0x45 0x00 0x87 0x80000000 if !($A & 0x80000000) goto L0341 L0206: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0207: 0x45 0x40 0x88 0xfff7b7ff if ($A & 0xfff7b7ff) goto L0272, else goto L0344 L0208: 0x06 0x00 0x00 0x00030008 return TRAP(8) L0209: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0210: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0214 L0211: 0x15 0x00 0x81 0xffffffff if ($A != 0xffffffff) goto L0341 L0212: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0213: 0x45 0x00 0x7f 0x80000000 if !($A & 0x80000000) goto L0341 L0214: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0215: 0x45 0x38 0x00 0x80000000 if ($A & 0x80000000) goto L0272 L0216: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0217: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0221 L0218: 0x15 0x00 0x7a 0xffffffff if ($A != 0xffffffff) goto L0341 L0219: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0220: 0x45 0x00 0x78 0x80000000 if !($A & 0x80000000) goto L0341 L0221: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0222: 0x15 0x79 0x00 0x00000007 if ($A == 0x7) goto L0344 L0223: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0224: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0228 L0225: 0x15 0x00 0x73 0xffffffff if ($A != 0xffffffff) goto L0341 L0226: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0227: 0x45 0x00 0x71 0x80000000 if !($A & 0x80000000) goto L0341 L0228: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0229: 0x15 0x72 0x00 0x00000001 if ($A == 0x1) goto L0344 L0230: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0231: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0235 L0232: 0x15 0x00 0x6c 0xffffffff if ($A != 0xffffffff) goto L0341 L0233: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0234: 0x45 0x00 0x6a 0x80000000 if !($A & 0x80000000) goto L0341 L0235: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0236: 0x15 0x6b 0x00 0x00000006 if ($A == 0x6) goto L0344 L0237: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0238: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0242 L0239: 0x15 0x00 0x65 0xffffffff if ($A != 0xffffffff) goto L0341 L0240: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0241: 0x45 0x00 0x63 0x80000000 if !($A & 0x80000000) goto L0341 L0242: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0243: 0x15 0x64 0x00 0x00000004 if ($A == 0x4) goto L0344 L0244: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0245: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0249 L0246: 0x15 0x00 0x5e 0xffffffff if ($A != 0xffffffff) goto L0341 L0247: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0248: 0x45 0x00 0x5c 0x80000000 if !($A & 0x80000000) goto L0341 L0249: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0250: 0x15 0x5d 0x00 0x00000002 if ($A == 0x2) goto L0344 L0251: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0252: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0256 L0253: 0x15 0x00 0x57 0xffffffff if ($A != 0xffffffff) goto L0341 L0254: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0255: 0x45 0x00 0x55 0x80000000 if !($A & 0x80000000) goto L0341 L0256: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0257: 0x15 0x56 0x00 0x00000000 if ($A == 0x0) goto L0344 L0258: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0259: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0263 L0260: 0x15 0x00 0x50 0xffffffff if ($A != 0xffffffff) goto L0341 L0261: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0262: 0x45 0x00 0x4e 0x80000000 if !($A & 0x80000000) goto L0341 L0263: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0264: 0x15 0x4f 0x00 0x00000005 if ($A == 0x5) goto L0344 L0265: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0266: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0270 L0267: 0x15 0x00 0x49 0xffffffff if ($A != 0xffffffff) goto L0341 L0268: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0269: 0x45 0x00 0x47 0x80000000 if !($A & 0x80000000) goto L0341 L0270: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0271: 0x15 0x48 0x00 0x00000003 if ($A == 0x3) goto L0344 L0272: 0x05 0x00 0x00 0x0000014b goto L0604 L0273: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0274: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0278 L0275: 0x15 0x00 0x41 0xffffffff if ($A != 0xffffffff) goto L0341 L0276: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0277: 0x45 0x00 0x3f 0x80000000 if !($A & 0x80000000) goto L0341 L0278: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0279: 0x45 0x00 0x40 0xfffffe7f if !($A & 0xfffffe7f) goto L0344 L0280: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0281: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0285 L0282: 0x15 0x00 0x3a 0xffffffff if ($A != 0xffffffff) goto L0341 L0283: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0284: 0x45 0x00 0x38 0x80000000 if !($A & 0x80000000) goto L0341 L0285: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0286: 0x54 0x00 0x00 0xfffffe7f $A &= 0xfffffe7f L0287: 0x15 0x38 0x00 0x00000001 if ($A == 0x1) goto L0344 L0288: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0289: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0293 L0290: 0x15 0x00 0x32 0xffffffff if ($A != 0xffffffff) goto L0341 L0291: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0292: 0x45 0x00 0x30 0x80000000 if !($A & 0x80000000) goto L0341 L0293: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0294: 0x54 0x00 0x00 0xfffffe7f $A &= 0xfffffe7f L0295: 0x15 0x30 0x00 0x00000003 if ($A == 0x3) goto L0344 L0296: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0297: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0301 L0298: 0x15 0x00 0x2a 0xffffffff if ($A != 0xffffffff) goto L0341 L0299: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0300: 0x45 0x00 0x28 0x80000000 if !($A & 0x80000000) goto L0341 L0301: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0302: 0x54 0x00 0x00 0xfffffe7f $A &= 0xfffffe7f L0303: 0x15 0x28 0x00 0x00000004 if ($A == 0x4) goto L0344 L0304: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0305: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0309 L0306: 0x15 0x00 0x22 0xffffffff if ($A != 0xffffffff) goto L0341 L0307: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0308: 0x45 0x00 0x20 0x80000000 if !($A & 0x80000000) goto L0341 L0309: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0310: 0x54 0x00 0x00 0xfffffe7f $A &= 0xfffffe7f L0311: 0x15 0x20 0x00 0x00000005 if ($A == 0x5) goto L0344 L0312: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0313: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0317 L0314: 0x15 0x00 0x1a 0xffffffff if ($A != 0xffffffff) goto L0341 L0315: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0316: 0x45 0x00 0x18 0x80000000 if !($A & 0x80000000) goto L0341 L0317: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0318: 0x54 0x00 0x00 0xfffffe7f $A &= 0xfffffe7f L0319: 0x15 0x18 0x00 0x00000009 if ($A == 0x9) goto L0344 L0320: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0321: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0325 L0322: 0x15 0x00 0x12 0xffffffff if ($A != 0xffffffff) goto L0341 L0323: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0324: 0x45 0x00 0x10 0x80000000 if !($A & 0x80000000) goto L0341 L0325: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0326: 0x54 0x00 0x00 0xfffffe7f $A &= 0xfffffe7f L0327: 0x15 0x10 0x00 0x0000000a if ($A == 0xa) goto L0344 L0328: 0x06 0x00 0x00 0x00050016 return ERRNO(22) L0329: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0330: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0334 L0331: 0x15 0x00 0x09 0xffffffff if ($A != 0xffffffff) goto L0341 L0332: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0333: 0x45 0x00 0x07 0x80000000 if !($A & 0x80000000) goto L0341 L0334: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0335: 0x15 0x08 0x00 0x00000010 if ($A == 0x10) goto L0344 L0336: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0337: 0x15 0x04 0x00 0x00000000 if ($A == 0x0) goto L0342 L0338: 0x15 0x00 0x02 0xffffffff if ($A != 0xffffffff) goto L0341 L0339: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0340: 0x45 0x01 0x00 0x80000000 if ($A & 0x80000000) goto L0342 L0341: 0x05 0x00 0x00 0x00000101 goto L0599 L0342: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0343: 0x15 0x00 0x01 0x0000000f if ($A != 0xf) goto L0345 L0344: 0x05 0x00 0x00 0x00000102 goto L0603 L0345: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0346: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0350 L0347: 0x15 0x00 0xfb 0xffffffff if ($A != 0xffffffff) goto L0599 L0348: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0349: 0x45 0x00 0xf9 0x80000000 if !($A & 0x80000000) goto L0599 L0350: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0351: 0x15 0xfb 0x00 0x00000003 if ($A == 0x3) goto L0603 L0352: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0353: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0357 L0354: 0x15 0x00 0xf4 0xffffffff if ($A != 0xffffffff) goto L0599 L0355: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0356: 0x45 0x00 0xf2 0x80000000 if !($A & 0x80000000) goto L0599 L0357: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0358: 0x15 0xf4 0x00 0x00000004 if ($A == 0x4) goto L0603 L0359: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0360: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0364 L0361: 0x15 0x00 0xed 0xffffffff if ($A != 0xffffffff) goto L0599 L0362: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0363: 0x45 0x00 0xeb 0x80000000 if !($A & 0x80000000) goto L0599 L0364: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0365: 0x15 0x07 0x00 0x53564d41 if ($A == 0x53564d41) goto L0373 L0366: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0367: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0371 L0368: 0x15 0x00 0xe6 0xffffffff if ($A != 0xffffffff) goto L0599 L0369: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0370: 0x45 0x00 0xe4 0x80000000 if !($A & 0x80000000) goto L0599 L0371: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0372: 0x15 0xe5 0x07 0x59616d61 if ($A == 0x59616d61) goto L0602, else goto L0380 L0373: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0374: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0378 L0375: 0x15 0x00 0xdf 0xffffffff if ($A != 0xffffffff) goto L0599 L0376: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0377: 0x45 0x00 0xdd 0x80000000 if !($A & 0x80000000) goto L0599 L0378: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0379: 0x15 0xdf 0x00 0x00000000 if ($A == 0x0) goto L0603 L0380: 0x06 0x00 0x00 0x00030007 return TRAP(7) L0381: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0382: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0386 L0383: 0x15 0x00 0xd7 0xffffffff if ($A != 0xffffffff) goto L0599 L0384: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0385: 0x45 0x00 0xd5 0x80000000 if !($A & 0x80000000) goto L0599 L0386: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0387: 0x15 0xd7 0x00 0x00000000 if ($A == 0x0) goto L0603 L0388: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0389: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0393 L0390: 0x15 0x00 0xd0 0xffffffff if ($A != 0xffffffff) goto L0599 L0391: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0392: 0x45 0x00 0xce 0x80000000 if !($A & 0x80000000) goto L0599 L0393: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0394: 0x15 0xd0 0x00 0x00000001 if ($A == 0x1) goto L0603 L0395: 0x06 0x00 0x00 0x00030006 return TRAP(6) L0396: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0397: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0401 L0398: 0x15 0x00 0xc8 0xffffffff if ($A != 0xffffffff) goto L0599 L0399: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0400: 0x45 0x00 0xc6 0x80000000 if !($A & 0x80000000) goto L0599 L0401: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0402: 0x15 0x00 0xc9 0x00000000 if ($A != 0x0) goto L0604 L0403: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0404: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0408 L0405: 0x15 0x00 0xc1 0xffffffff if ($A != 0xffffffff) goto L0599 L0406: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0407: 0x45 0x00 0xbf 0x80000000 if !($A & 0x80000000) goto L0599 L0408: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0409: 0x15 0xc1 0x00 0x00000000 if ($A == 0x0) goto L0603 L0410: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0411: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0415 L0412: 0x15 0x00 0xba 0xffffffff if ($A != 0xffffffff) goto L0599 L0413: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0414: 0x45 0x00 0xb8 0x80000000 if !($A & 0x80000000) goto L0599 L0415: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0416: 0x15 0xba 0xb9 0x00000001 if ($A == 0x1) goto L0603, else goto L0602 L0417: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0418: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0422 L0419: 0x15 0x00 0xb3 0xffffffff if ($A != 0xffffffff) goto L0599 L0420: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0421: 0x45 0x00 0xb1 0x80000000 if !($A & 0x80000000) goto L0599 L0422: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0423: 0x15 0xb3 0x00 0x00000003 if ($A == 0x3) goto L0603 L0424: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0425: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0429 L0426: 0x15 0x00 0xac 0xffffffff if ($A != 0xffffffff) goto L0599 L0427: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0428: 0x45 0x00 0xaa 0x80000000 if !($A & 0x80000000) goto L0599 L0429: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0430: 0x15 0xac 0x00 0x00000001 if ($A == 0x1) goto L0603 L0431: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0432: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0436 L0433: 0x15 0x00 0xa5 0xffffffff if ($A != 0xffffffff) goto L0599 L0434: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0435: 0x45 0x00 0xa3 0x80000000 if !($A & 0x80000000) goto L0599 L0436: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0437: 0x15 0xa5 0x00 0x0000040a if ($A == 0x40a) goto L0603 L0438: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0439: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0443 L0440: 0x15 0x00 0x9e 0xffffffff if ($A != 0xffffffff) goto L0599 L0441: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0442: 0x45 0x00 0x9c 0x80000000 if !($A & 0x80000000) goto L0599 L0443: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0444: 0x15 0x9e 0x00 0x00000002 if ($A == 0x2) goto L0603 L0445: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0446: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0450 L0447: 0x15 0x00 0x97 0xffffffff if ($A != 0xffffffff) goto L0599 L0448: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0449: 0x45 0x00 0x95 0x80000000 if !($A & 0x80000000) goto L0599 L0450: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0451: 0x15 0x97 0x00 0x00000006 if ($A == 0x6) goto L0603 L0452: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0453: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0457 L0454: 0x15 0x00 0x90 0xffffffff if ($A != 0xffffffff) goto L0599 L0455: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0456: 0x45 0x00 0x8e 0x80000000 if !($A & 0x80000000) goto L0599 L0457: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0458: 0x15 0x90 0x00 0x00000007 if ($A == 0x7) goto L0603 L0459: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0460: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0464 L0461: 0x15 0x00 0x89 0xffffffff if ($A != 0xffffffff) goto L0599 L0462: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0463: 0x45 0x00 0x87 0x80000000 if !($A & 0x80000000) goto L0599 L0464: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0465: 0x15 0x89 0x00 0x00000005 if ($A == 0x5) goto L0603 L0466: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0467: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0471 L0468: 0x15 0x00 0x82 0xffffffff if ($A != 0xffffffff) goto L0599 L0469: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0470: 0x45 0x00 0x80 0x80000000 if !($A & 0x80000000) goto L0599 L0471: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0472: 0x15 0x82 0x00 0x00000000 if ($A == 0x0) goto L0603 L0473: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0474: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0478 L0475: 0x15 0x00 0x7b 0xffffffff if ($A != 0xffffffff) goto L0599 L0476: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0477: 0x45 0x00 0x79 0x80000000 if !($A & 0x80000000) goto L0599 L0478: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0479: 0x15 0x7b 0x00 0x00000406 if ($A == 0x406) goto L0603 L0480: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0481: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0485 L0482: 0x15 0x00 0x74 0xffffffff if ($A != 0xffffffff) goto L0599 L0483: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0484: 0x45 0x00 0x72 0x80000000 if !($A & 0x80000000) goto L0599 L0485: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0486: 0x15 0x09 0x00 0x00000004 if ($A == 0x4) goto L0496 L0487: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0488: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0492 L0489: 0x15 0x00 0x6d 0xffffffff if ($A != 0xffffffff) goto L0599 L0490: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0491: 0x45 0x00 0x6b 0x80000000 if !($A & 0x80000000) goto L0599 L0492: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0493: 0x15 0x00 0x6e 0x00000409 if ($A != 0x409) goto L0604 L0494: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0495: 0x15 0x60 0x6c 0x00000000 if ($A == 0x0) goto L0592, else goto L0604 L0496: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0497: 0x15 0x00 0x6a 0x00000000 if ($A != 0x0) goto L0604 L0498: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0499: 0x45 0x68 0x67 0xffe363fc if ($A & 0xffe363fc) goto L0604, else goto L0603 L0500: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0501: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0505 L0502: 0x15 0x00 0x60 0xffffffff if ($A != 0xffffffff) goto L0599 L0503: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0504: 0x45 0x00 0x5e 0x80000000 if !($A & 0x80000000) goto L0599 L0505: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0506: 0x15 0x60 0x00 0x00000001 if ($A == 0x1) goto L0603 L0507: 0x06 0x00 0x00 0x00030005 return TRAP(5) L0508: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0509: 0x15 0x00 0x02 0x00000000 if ($A != 0x0) goto L0512 L0510: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0511: 0x15 0x5b 0x00 0x003d0f00 if ($A == 0x3d0f00) goto L0603 L0512: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0513: 0x45 0x00 0x58 0x00010100 if !($A & 0x10100) goto L0602 L0514: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0515: 0x54 0x00 0x00 0x00004100 $A &= 0x4100 L0516: 0x15 0x55 0x00 0x00004100 if ($A == 0x4100) goto L0602 L0517: 0x06 0x00 0x00 0x00030004 return TRAP(4) L0518: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0519: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0523 L0520: 0x15 0x00 0x4e 0xffffffff if ($A != 0xffffffff) goto L0599 L0521: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0522: 0x45 0x00 0x4c 0x80000000 if !($A & 0x80000000) goto L0599 L0523: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0524: 0x15 0x00 0x4f 0x00000001 if ($A != 0x1) goto L0604 L0525: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0526: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0530 L0527: 0x15 0x00 0x47 0xffffffff if ($A != 0xffffffff) goto L0599 L0528: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0529: 0x45 0x00 0x45 0x80000000 if !($A & 0x80000000) goto L0599 L0530: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0531: 0x15 0x47 0x48 0x0000002a if ($A == 0x2a) goto L0603, else goto L0604 L0532: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0533: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0537 L0534: 0x15 0x00 0x40 0xffffffff if ($A != 0xffffffff) goto L0599 L0535: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0536: 0x45 0x00 0x3e 0x80000000 if !($A & 0x80000000) goto L0599 L0537: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0538: 0x15 0x40 0x41 0x00000001 if ($A == 0x1) goto L0603, else goto L0604 L0539: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0540: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0544 L0541: 0x15 0x00 0x39 0xffffffff if ($A != 0xffffffff) goto L0599 L0542: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0543: 0x45 0x00 0x37 0x80000000 if !($A & 0x80000000) goto L0599 L0544: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0545: 0x15 0x39 0x00 0x00000004 if ($A == 0x4) goto L0603 L0546: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0547: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0551 L0548: 0x15 0x00 0x32 0xffffffff if ($A != 0xffffffff) goto L0599 L0549: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0550: 0x45 0x00 0x30 0x80000000 if !($A & 0x80000000) goto L0599 L0551: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0552: 0x15 0x32 0x00 0x00000001 if ($A == 0x1) goto L0603 L0553: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0554: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0558 L0555: 0x15 0x00 0x2b 0xffffffff if ($A != 0xffffffff) goto L0599 L0556: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0557: 0x45 0x00 0x29 0x80000000 if !($A & 0x80000000) goto L0599 L0558: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0559: 0x15 0x2b 0x00 0x00000009 if ($A == 0x9) goto L0603 L0560: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0561: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0565 L0562: 0x15 0x00 0x24 0xffffffff if ($A != 0xffffffff) goto L0599 L0563: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0564: 0x45 0x00 0x22 0x80000000 if !($A & 0x80000000) goto L0599 L0565: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0566: 0x15 0x24 0x00 0x00000000 if ($A == 0x0) goto L0603 L0567: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0568: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0572 L0569: 0x15 0x00 0x1d 0xffffffff if ($A != 0xffffffff) goto L0599 L0570: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0571: 0x45 0x00 0x1b 0x80000000 if !($A & 0x80000000) goto L0599 L0572: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0573: 0x15 0x1d 0x1c 0x00000008 if ($A == 0x8) goto L0603, else goto L0602 L0574: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0575: 0x15 0x00 0x02 0x00000000 if ($A != 0x0) goto L0578 L0576: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0577: 0x15 0x19 0x00 0x00005401 if ($A == 0x5401) goto L0603 L0578: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0579: 0x15 0x00 0x02 0x00000000 if ($A != 0x0) goto L0582 L0580: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0581: 0x15 0x15 0x00 0x0000541b if ($A == 0x541b) goto L0603 L0582: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0583: 0x15 0x00 0x02 0x00000000 if ($A != 0x0) goto L0586 L0584: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0585: 0x15 0x11 0x00 0x40086200 if ($A == 0x40086200) goto L0603 L0586: 0x06 0x00 0x00 0x00030003 return TRAP(3) L0587: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0588: 0x15 0x03 0x00 0x00000000 if ($A == 0x0) goto L0592 L0589: 0x15 0x00 0x09 0xffffffff if ($A != 0xffffffff) goto L0599 L0590: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0591: 0x45 0x00 0x07 0x80000000 if !($A & 0x80000000) goto L0599 L0592: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0593: 0x45 0x0a 0x09 0xfffffff8 if ($A & 0xfffffff8) goto L0604, else goto L0603 L0594: 0x20 0x00 0x00 0x0000002c $A = $high_args[3] L0595: 0x15 0x04 0x00 0x00000000 if ($A == 0x0) goto L0600 L0596: 0x15 0x00 0x02 0xffffffff if ($A != 0xffffffff) goto L0599 L0597: 0x20 0x00 0x00 0x00000028 $A = $low_args[3] L0598: 0x45 0x01 0x00 0x80000000 if ($A & 0x80000000) goto L0600 L0599: 0x06 0x00 0x00 0x00030002 return TRAP(2) L0600: 0x20 0x00 0x00 0x00000028 $A = $low_args[3] L0601: 0x45 0x02 0x01 0xfffd97cc if ($A & 0xfffd97cc) goto L0604, else goto L0603 L0602: 0x06 0x00 0x00 0x00050001 return ERRNO(1) L0603: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0604: 0x06 0x00 0x00 0x00030001 return TRAP(1) #--------------------------------- ceccomp-4.0/test/text/gctf-2019-quals-caas000066400000000000000000000114721514205130000202140ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x3c 0xc000003e if ($A != x86_64) goto L0063 L0003: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0004: 0x35 0x3a 0x00 0x40000000 if ($A >= 0x40000000) goto L0063 L0005: 0x15 0x38 0x00 0x00000000 if ($A == read) goto L0062 L0006: 0x15 0x37 0x00 0x00000001 if ($A == write) goto L0062 L0007: 0x15 0x36 0x00 0x00000003 if ($A == close) goto L0062 L0008: 0x15 0x35 0x00 0x0000000b if ($A == munmap) goto L0062 L0009: 0x15 0x34 0x00 0x00000018 if ($A == sched_yield) goto L0062 L0010: 0x15 0x33 0x00 0x00000020 if ($A == dup) goto L0062 L0011: 0x15 0x32 0x00 0x00000021 if ($A == dup2) goto L0062 L0012: 0x15 0x31 0x00 0x00000023 if ($A == nanosleep) goto L0062 L0013: 0x15 0x30 0x00 0x0000002a if ($A == connect) goto L0062 L0014: 0x15 0x2f 0x00 0x0000002b if ($A == accept) goto L0062 L0015: 0x15 0x2e 0x00 0x0000002f if ($A == recvmsg) goto L0062 L0016: 0x15 0x2d 0x00 0x00000031 if ($A == bind) goto L0062 L0017: 0x15 0x2c 0x00 0x0000003c if ($A == exit) goto L0062 L0018: 0x15 0x2b 0x00 0x000000e7 if ($A == exit_group) goto L0062 L0019: 0x15 0x00 0x04 0x00000038 if ($A != clone) goto L0024 L0020: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0021: 0x15 0x00 0x29 0x00000000 if ($A != 0x0) goto L0063 L0022: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0023: 0x15 0x26 0x27 0x00010900 if ($A == 0x10900) goto L0062, else goto L0063 L0024: 0x15 0x00 0x0c 0x00000029 if ($A != socket) goto L0037 L0025: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0026: 0x15 0x00 0x24 0x00000000 if ($A != 0x0) goto L0063 L0027: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0028: 0x15 0x00 0x22 0x00000002 if ($A != 0x2) goto L0063 L0029: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0030: 0x15 0x00 0x20 0x00000000 if ($A != 0x0) goto L0063 L0031: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0032: 0x15 0x00 0x1e 0x00000001 if ($A != 0x1) goto L0063 L0033: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0034: 0x15 0x00 0x1c 0x00000000 if ($A != 0x0) goto L0063 L0035: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0036: 0x15 0x19 0x1a 0x00000000 if ($A == 0x0) goto L0062, else goto L0063 L0037: 0x15 0x00 0x19 0x00000009 if ($A != mmap) goto L0063 L0038: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0039: 0x15 0x00 0x17 0x00000000 if ($A != 0x0) goto L0063 L0040: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0041: 0x15 0x00 0x15 0x00000000 if ($A != 0x0) goto L0063 L0042: 0x20 0x00 0x00 0x0000001c $A = $high_args[1] L0043: 0x15 0x00 0x13 0x00000000 if ($A != 0x0) goto L0063 L0044: 0x20 0x00 0x00 0x00000018 $A = $low_args[1] L0045: 0x15 0x00 0x11 0x00001000 if ($A != 0x1000) goto L0063 L0046: 0x20 0x00 0x00 0x00000024 $A = $high_args[2] L0047: 0x15 0x00 0x0f 0x00000000 if ($A != 0x0) goto L0063 L0048: 0x20 0x00 0x00 0x00000020 $A = $low_args[2] L0049: 0x15 0x00 0x0d 0x00000003 if ($A != 0x3) goto L0063 L0050: 0x20 0x00 0x00 0x0000002c $A = $high_args[3] L0051: 0x15 0x00 0x0b 0x00000000 if ($A != 0x0) goto L0063 L0052: 0x20 0x00 0x00 0x00000028 $A = $low_args[3] L0053: 0x15 0x00 0x09 0x00000022 if ($A != 0x22) goto L0063 L0054: 0x20 0x00 0x00 0x00000034 $A = $high_args[4] L0055: 0x15 0x00 0x07 0x00000000 if ($A != 0x0) goto L0063 L0056: 0x20 0x00 0x00 0x00000030 $A = $low_args[4] L0057: 0x15 0x00 0x05 0x00000000 if ($A != 0x0) goto L0063 L0058: 0x20 0x00 0x00 0x0000003c $A = $high_args[5] L0059: 0x15 0x00 0x03 0x00000000 if ($A != 0x0) goto L0063 L0060: 0x20 0x00 0x00 0x00000038 $A = $low_args[5] L0061: 0x15 0x00 0x01 0x00000000 if ($A != 0x0) goto L0063 L0062: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0063: 0x06 0x00 0x00 0x00000000 return KILL #--------------------------------- ceccomp-4.0/test/text/ld_alu_misc000066400000000000000000000030051514205130000171140ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000008 $A = $low_pc L0002: 0x07 0x00 0x00 0x00000000 $X = $A L0003: 0x20 0x00 0x00 0x0000000c $A = $high_pc L0004: 0x20 0x00 0x00 0x00000014 $A = $high_args[0] L0005: 0x20 0x00 0x00 0x00000038 $A = $low_args[5] L0006: 0x0c 0x00 0x00 0x00000000 $A += $X L0007: 0x54 0x00 0x00 0x00000fff $A &= 0xfff L0008: 0x15 0x01 0x00 0x00000000 if ($A == 0x0) goto L0010 L0009: 0x06 0x00 0x00 0x00000000 return KILL L0010: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0011: 0x07 0x00 0x00 0x00000000 $X = $A L0012: 0x24 0x00 0x00 0x00000102 $A *= 0x102 L0013: 0x02 0x00 0x00 0x00000000 $mem[0x0] = $A L0014: 0x87 0x00 0x00 0x00000000 $A = $X L0015: 0x24 0x00 0x00 0x000001d2 $A *= 0x1d2 L0016: 0x84 0x00 0x00 0x00000000 $A = -$A L0017: 0x04 0x00 0x00 0x00001337 $A += 0x1337 L0018: 0x07 0x00 0x00 0x00000000 $X = $A L0019: 0x60 0x00 0x00 0x00000000 $A = $mem[0x0] L0020: 0x1d 0x00 0x01 0x00000000 if ($A != $X) goto L0022 L0021: 0x06 0x00 0x00 0x00000000 return KILL L0022: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- ceccomp-4.0/test/text/libseccomp000066400000000000000000000016251514205130000167670ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x08 0xc000003e if ($A != x86_64) goto L0011 L0003: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0004: 0x35 0x06 0x00 0x40000000 if ($A >= 0x40000000) goto L0011 L0005: 0x15 0x04 0x00 0x00000142 if ($A == execveat) goto L0010 L0006: 0x15 0x03 0x00 0x0000003b if ($A == execve) goto L0010 L0007: 0x15 0x02 0x00 0x00000048 if ($A == fcntl) goto L0010 L0008: 0x15 0x01 0x00 0x0000003c if ($A == exit) goto L0010 L0009: 0x06 0x00 0x00 0x00050005 return ERRNO(5) L0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW L0011: 0x06 0x00 0x00 0x00000000 return KILL #--------------------------------- ceccomp-4.0/test/text/multi_arch000066400000000000000000000040161514205130000167730ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x05 0xc000003e if ($A != x86_64) goto L0008 L0003: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0004: 0x35 0x12 0x00 0x40000000 if ($A >= 0x40000000) goto L0023 L0005: 0x15 0x12 0x00 0x0000002b if ($A == accept) goto L0024 L0006: 0x15 0x11 0x00 0x00000032 if ($A == listen) goto L0024 L0007: 0x15 0x10 0x0f 0x00000031 if ($A == bind) goto L0024, else goto L0023 L0008: 0x15 0x00 0x04 0x40000003 if ($A != i386) goto L0013 L0009: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0010: 0x15 0x0d 0x00 0x00000000 if ($A == i386.restart_syscall) goto L0024 L0011: 0x15 0x0c 0x00 0x00000001 if ($A == i386.exit) goto L0024 L0012: 0x15 0x0b 0x0a 0x00000002 if ($A == i386.fork) goto L0024, else goto L0023 L0013: 0x15 0x00 0x04 0xc00000b7 if ($A != aarch64) goto L0018 L0014: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0015: 0x15 0x08 0x00 0xffffd85c if ($A == aarch64.alarm) goto L0024 L0016: 0x15 0x07 0x00 0xffffd88b if ($A == aarch64.select) goto L0024 L0017: 0x15 0x06 0x05 0xffffd848 if ($A == aarch64.pipe) goto L0024, else goto L0023 L0018: 0x15 0x00 0x04 0x40000028 if ($A != arm) goto L0023 L0019: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0020: 0x15 0x03 0x00 0x00000000 if ($A == arm.restart_syscall) goto L0024 L0021: 0x15 0x02 0x00 0x00000091 if ($A == arm.readv) goto L0024 L0022: 0x15 0x01 0x00 0x00000078 if ($A == arm.clone) goto L0024 L0023: 0x06 0x00 0x00 0x00000000 return KILL L0024: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- ceccomp-4.0/test/text/twctf-2016-diary000066400000000000000000000024661514205130000174760ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0002: 0x15 0x00 0x01 0x00000002 if ($A != open) goto L0004 L0003: 0x06 0x00 0x00 0x00000000 return KILL L0004: 0x15 0x00 0x01 0x00000101 if ($A != openat) goto L0006 L0005: 0x06 0x00 0x00 0x00000000 return KILL L0006: 0x15 0x00 0x01 0x0000003b if ($A != execve) goto L0008 L0007: 0x06 0x00 0x00 0x00000000 return KILL L0008: 0x15 0x00 0x01 0x00000038 if ($A != clone) goto L0010 L0009: 0x06 0x00 0x00 0x00000000 return KILL L0010: 0x15 0x00 0x01 0x00000039 if ($A != fork) goto L0012 L0011: 0x06 0x00 0x00 0x00000000 return KILL L0012: 0x15 0x00 0x01 0x0000003a if ($A != vfork) goto L0014 L0013: 0x06 0x00 0x00 0x00000000 return KILL L0014: 0x15 0x00 0x01 0x00000055 if ($A != creat) goto L0016 L0015: 0x06 0x00 0x00 0x00000000 return KILL L0016: 0x15 0x00 0x01 0x00000142 if ($A != execveat) goto L0018 L0017: 0x06 0x00 0x00 0x00000000 return KILL L0018: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- ceccomp-4.0/test/text/x32000066400000000000000000000020011514205130000152500ustar00rootroot00000000000000#Label CODE JT JF K #--------------------------------- L0001: 0x20 0x00 0x00 0x00000004 $A = $arch L0002: 0x15 0x00 0x09 0x40000003 if ($A != i386) goto L0012 L0003: 0x20 0x00 0x00 0x00000000 $A = $syscall_nr L0004: 0x25 0x00 0x07 0x40000000 if ($A <= 0x40000000) goto L0012 L0005: 0x15 0x06 0x00 0x40000000 if ($A == 0x40000000) goto L0012 L0006: 0x15 0x05 0x00 0x40000001 if ($A == 0x40000001) goto L0012 L0007: 0x15 0x04 0x00 0x400000ac if ($A == 0x400000ac) goto L0012 L0008: 0x15 0x00 0x03 0x40000009 if ($A != 0x40000009) goto L0012 L0009: 0x20 0x00 0x00 0x00000010 $A = $low_args[0] L0010: 0x15 0x01 0x00 0x00000000 if ($A == 0x0) goto L0012 L0011: 0x06 0x00 0x00 0x00050005 return ERRNO(5) L0012: 0x06 0x00 0x00 0x7fff0000 return ALLOW #--------------------------------- ceccomp-4.0/test/unit_test.c000066400000000000000000000050551514205130000161230ustar00rootroot00000000000000// this is for separately function testing #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) enum test_case { TEST_TRACE = 0, TEST_PROBE = 1, TEST_SEIZE = 2, TEST_TRACE_PID = 3, }; static void dont_handle (int sig) { (void)sig; } static const filter filters[] = { BPF_STMT (BPF_LD | BPF_W | BPF_ABS, (offsetof (seccomp_data, nr))), BPF_JUMP (BPF_JMP | BPF_JEQ | BPF_K, SYS_execve, 1, 0), BPF_STMT (BPF_RET | BPF_K, SECCOMP_RET_ALLOW), BPF_STMT (BPF_RET | BPF_K, SECCOMP_RET_ERRNO | 1), }; static void load_filter (bool tofail) { prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); struct sock_fprog prog = { .len = ARRAY_SIZE (filters), .filter = (filter *)filters }; syscall (SYS_seccomp, SECCOMP_SET_MODE_FILTER, NULL, &prog); // test failed loading if (tofail) syscall (SYS_seccomp, SECCOMP_SET_MODE_FILTER, NULL, NULL); } int main (int argc, char **argv) { setvbuf (stdout, NULL, _IOLBF, 0x100); int choice = argc < 2 ? 0 : atoi (argv[1]); struct sigaction sa = { 0 }; sa.sa_handler = dont_handle; switch (choice) { case TEST_TRACE: load_filter (true); break; case TEST_SEIZE: prctl (PR_SET_PTRACER, PR_SET_PTRACER_ANY); pid_t pid = getpid (); sigaction (SIGCONT, &sa, NULL); printf ("pid=%d\n", pid); pause (); // waiting SIGCONT pid = fork (); if (pid) { waitpid (pid, NULL, 0); exit (0); } // child load_filter (false); printf ("child=%d\n", getpid ()); pause (); break; case TEST_PROBE: pid = fork (); if (pid != 0) { wait (NULL); exit (0); } else { pid = fork (); if (pid != 0) exit (0); // grandchild process pid = getpid (); printf ("pid=%d\n", pid); load_filter (false); signal (SIGINT, SIG_IGN); sleep (100); } break; case TEST_TRACE_PID: sigaction (SIGCONT, &sa, NULL); load_filter (false); printf ("pid=%d\n", getpid ()); pause (); break; default: load_filter (true); break; } return 0; }