├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── fedora-dependency.sh ├── include ├── linux │ ├── bpf.h │ ├── bpf_local_storage.h │ ├── bpf_lsm.h │ └── bpf_types.h └── uapi │ └── linux │ └── bpf.h ├── kernel └── bpf │ ├── Makefile │ ├── bpf_cred_storage.c │ ├── bpf_file_storage.c │ ├── bpf_ipc_storage.c │ ├── bpf_lsm.c │ ├── bpf_msg_storage.c │ ├── syscall.c │ └── verifier.c ├── scripts └── bpf_helpers_doc.py ├── security └── bpf │ └── hooks.c └── tools ├── bpf └── bpftool │ ├── Documentation │ └── bpftool-map.rst │ ├── bash-completion │ └── bpftool │ └── map.c ├── include └── uapi │ └── linux │ └── bpf.h ├── lib └── bpf │ └── libbpf_probes.c └── testing └── selftests └── bpf ├── prog_tests └── test_local_storage.c └── progs └── local_storage.c /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference 2 | version: 2.1 3 | jobs: 4 | checkout: 5 | docker: 6 | - image: kernelci/build-gcc-8_x86:latest 7 | working_directory: ~/build 8 | steps: 9 | - checkout 10 | - restore_cache: 11 | keys: 12 | - build-{{ checksum "Makefile" }} 13 | - run: 14 | name: 'Prepare build environment...' 15 | command: | 16 | if [ -d "~/build/linux-stable" ]; then 17 | echo 'Build environment was cached.' 18 | else 19 | echo 'Build environment was not cached.' 20 | apt-get update -qq --allow-releaseinfo-change 21 | apt-get install patch 22 | apt-get install --reinstall make 23 | make prepare 24 | make save_space 25 | fi 26 | - save_cache: 27 | key: build-{{ checksum "Makefile" }} 28 | paths: 29 | - build 30 | - persist_to_workspace: 31 | root: ~/build 32 | paths: 33 | - linux-stable 34 | - pristine 35 | compile: 36 | docker: 37 | - image: kernelci/build-gcc-8_x86:latest 38 | working_directory: ~/build 39 | steps: 40 | - checkout 41 | - attach_workspace: 42 | at: ~/build 43 | - run: 'apt-get update -qq --allow-releaseinfo-change' 44 | - run: 'apt-get install -y sudo rpm cpio' 45 | - run: 'apt-get install -y build-essential rsync libtool libncurses5-dev' 46 | - run: 'apt-get install -y libncursesw5-dev bc pkg-config zsh libelf-dev' 47 | - run: 'apt-get install -y bison flex' 48 | - run: 'make config_circle' 49 | - run: 'make build' 50 | compile_camflow: 51 | docker: 52 | - image: kernelci/build-gcc-8_x86:latest 53 | working_directory: ~/build 54 | steps: 55 | - checkout 56 | - run: 'apt-get update -qq --allow-releaseinfo-change' 57 | - run: 'apt-get install -y sudo rpm cpio' 58 | - run: 'apt-get install -y build-essential rsync libtool libncurses5-dev' 59 | - run: 'apt-get install -y libncursesw5-dev bc pkg-config zsh libelf-dev' 60 | - run: 'apt-get install -y bison flex' 61 | - run: 'git config --global user.name "Soo Yee Lim"' 62 | - run: 'git config --global user.email "sooyee.lim@bristol.ac.uk"' 63 | - run: 'make prepare_camflow' 64 | - run: 'make config_circle_camflow' 65 | - run: 'make build' 66 | patch: 67 | docker: 68 | - image: kernelci/build-gcc-8_x86:latest 69 | working_directory: ~/build 70 | steps: 71 | - checkout 72 | - attach_workspace: 73 | at: ~/build 74 | - run: 'apt-get update -qq --allow-releaseinfo-change' 75 | - run: 'apt-get install -y sudo rpm cpio' 76 | - run: 'apt-get install -y build-essential rsync libtool libncurses5-dev' 77 | - run: 'apt-get install -y libncursesw5-dev bc pkg-config zsh libelf-dev' 78 | - run: 'apt-get install -y bison flex' 79 | - run: 'git config --global user.name "Soo Yee Lim"' 80 | - run: 'git config --global user.email "sooyee.lim@bristol.ac.uk"' 81 | - run: 'make patch' 82 | - persist_to_workspace: 83 | root: ~/build 84 | paths: 85 | - patches 86 | workflows: 87 | version: 2 88 | build-and-deploy: 89 | jobs: 90 | - checkout 91 | - compile_camflow 92 | - compile: 93 | requires: 94 | - checkout 95 | - patch: 96 | requires: 97 | - checkout 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | #temp files 32 | *~ 33 | *.db 34 | *.tmp 35 | 36 | #build directory 37 | build/* 38 | 39 | # patches directory 40 | patches/* 41 | 42 | # Debug files 43 | *.dSYM/ 44 | 45 | .config 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | kernel-version=5.11.2 2 | provbpf-version=0.1.0 3 | camflow-version=0.7.2 4 | arch=x86_64 5 | 6 | prepare: 7 | mkdir -p ~/build 8 | cd ~/build && git clone -b v$(kernel-version) --single-branch --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git 9 | cd ~/build/linux-stable && $(MAKE) mrproper 10 | cd ~/build && mkdir -p pristine 11 | cd ~/build && cp -r ./linux-stable ./pristine 12 | cd ~/build/linux-stable && sed -i -e "s/EXTRAVERSION =/EXTRAVERSION = provbpf$(provbpf-version)/g" Makefile 13 | 14 | delete: 15 | cd ~/build && sudo rm -rf ./pristine 16 | cd ~/build && sudo rm -rf ./linux-stable 17 | 18 | copy_change: 19 | cp -r ./kernel ~/build/linux-stable 20 | cp -r ./include ~/build/linux-stable 21 | cp -r ./tools ~/build/linux-stable 22 | cp -r ./security ~/build/linux-stable 23 | cp -r ./scripts ~/build/linux-stable 24 | 25 | config: copy_change 26 | cp -f /boot/config-$(shell uname -r) ~/build/linux-stable/.config 27 | cd ~/build/linux-stable && ./scripts/kconfig/streamline_config.pl > config_strip 28 | cd ~/build/linux-stable && cp -f config_strip .config 29 | cd ~/build/linux-stable && sed -i -e "s/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor\"/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf\"/g" .config 30 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_BPF_LSM is not set/CONFIG_BPF_LSM=y/g" .config 31 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_ADVANCED_ROUTER is not set/CONFIG_IP_ADVANCED_ROUTER=y/g" .config 32 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_MULTIPLE_TABLES is not set/CONFIG_IP_MULTIPLE_TABLES=y/g" .config 33 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK is not set/CONFIG_NETFILTER_NETLINK=y/g" .config 34 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_QUEUE is not set/CONFIG_NETFILTER_NETLINK_QUEUE=y/g" .config 35 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_ACCT is not set/CONFIG_NETFILTER_NETLINK_ACCT=y/g" .config 36 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_LOG is not set/CONFIG_NETFILTER_NETLINK_LOG=y/g" .config 37 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CT_NETLINK is not set/CONFIG_NF_CT_NETLINK=y/g" .config 38 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_TARGET_MARK is not set/CONFIG_NETFILTER_XT_TARGET_MARK=y/g" .config 39 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCHED is not set/CONFIG_NET_SCHED=y/g" .config 40 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCH_INGRESS is not set/CONFIG_NET_SCH_INGRESS=y/g" .config 41 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 42 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 43 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_FC_ATTRS is not set/CONFIG_SCSI_FC_ATTRS=y/g" .config 44 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI is not set/CONFIG_SCSI=y/g" .config 45 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CONNTRACK is not set/CONFIG_NF_CONNTRACK=y/g" .config 46 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_MARK is not set/CONFIG_NETFILTER_XT_MARK=y/g" .config 47 | cd ~/build/linux-stable && echo "CONFIG_NF_CT_NETLINK=y" >> .config 48 | cd ~/build/linux-stable && echo "CONFIG_SCSI_NETLINK=y" >> .config 49 | cd ~/build/linux-stable && $(MAKE) menuconfig 50 | cp -f ~/build/linux-stable/.config .config 51 | 52 | config_circle: copy_change 53 | cd ~/build/linux-stable && $(MAKE) olddefconfig 54 | cd ~/build/linux-stable && sed -i -e "s/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor\"/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf\"/g" .config 55 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_BPF_LSM is not set/CONFIG_BPF_LSM=y/g" .config 56 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_ADVANCED_ROUTER is not set/CONFIG_IP_ADVANCED_ROUTER=y/g" .config 57 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_MULTIPLE_TABLES is not set/CONFIG_IP_MULTIPLE_TABLES=y/g" .config 58 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK is not set/CONFIG_NETFILTER_NETLINK=y/g" .config 59 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_QUEUE is not set/CONFIG_NETFILTER_NETLINK_QUEUE=y/g" .config 60 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_ACCT is not set/CONFIG_NETFILTER_NETLINK_ACCT=y/g" .config 61 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_LOG is not set/CONFIG_NETFILTER_NETLINK_LOG=y/g" .config 62 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CT_NETLINK is not set/CONFIG_NF_CT_NETLINK=y/g" .config 63 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_TARGET_MARK is not set/CONFIG_NETFILTER_XT_TARGET_MARK=y/g" .config 64 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCHED is not set/CONFIG_NET_SCHED=y/g" .config 65 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCH_INGRESS is not set/CONFIG_NET_SCH_INGRESS=y/g" .config 66 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 67 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 68 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_FC_ATTRS is not set/CONFIG_SCSI_FC_ATTRS=y/g" .config 69 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI is not set/CONFIG_SCSI=y/g" .config 70 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CONNTRACK is not set/CONFIG_NF_CONNTRACK=y/g" .config 71 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_MARK is not set/CONFIG_NETFILTER_XT_MARK=y/g" .config 72 | cd ~/build/linux-stable && echo "CONFIG_NF_CT_NETLINK=y" >> .config 73 | cd ~/build/linux-stable && echo "CONFIG_SCSI_NETLINK=y" >> .config 74 | cp -f ~/build/linux-stable/.config .config 75 | 76 | build_kernel_sub: copy_change 77 | cd ~/build/linux-stable && $(MAKE) kernel W=1 78 | 79 | build_kernel: 80 | cd ~/build/linux-stable && $(MAKE) -j16 ARCH=${arch} 81 | 82 | build_libbpf: 83 | cd ~/build/linux-stable/tools/lib/bpf && sudo $(MAKE) all 84 | 85 | build_resolve_btfids: 86 | cd ~/build/linux-stable/tools/bpf/resolve_btfids && sudo $(MAKE) all 87 | 88 | build_bpftool: 89 | cd ~/build/linux-stable/tools/bpf/bpftool && sudo $(MAKE) all 90 | 91 | build_bpf: build_libbpf build_resolve_btfids build_bpftool 92 | 93 | build: build_kernel_sub build_kernel build_bpf 94 | 95 | install_header: 96 | cd ~/build/linux-stable && sudo $(MAKE) headers_install ARCH=${arch} INSTALL_HDR_PATH=/usr 97 | 98 | install_kernel: 99 | cd ~/build/linux-stable && sudo $(MAKE) modules_install ARCH=${arch} 100 | cd ~/build/linux-stable && sudo $(MAKE) install ARCH=${arch} 101 | cd ~/build/linux-stable && sudo cp -f .config /boot/config-$(kernel-version)provbpf$(provbpf-version)+ 102 | 103 | install_libbpf: 104 | cd ~/build/linux-stable/tools/lib/bpf && sudo $(MAKE) install 105 | 106 | install_bpftool: 107 | cd ~/build/linux-stable/tools/bpf/bpftool && sudo $(MAKE) install 108 | 109 | install_bpf: install_libbpf install_bpftool 110 | 111 | install: install_header install_kernel install_bpf 112 | 113 | clean: 114 | cd ~/build/linux-stable && $(MAKE) clean 115 | cd ~/build/linux-stable && $(MAKE) mrproper 116 | 117 | save_space: 118 | cd ~/build/linux-stable && rm -rf .git 119 | cd ~/build/pristine/linux-stable && rm -rf .git 120 | 121 | update_version: delete prepare 122 | mv include/linux/bpf.h include/linux/_bpf.h 123 | cp ~/build/pristine/linux-stable/include/linux/bpf.h include/linux/bpf.h 124 | mv include/uapi/linux/bpf.h include/uapi/linux/_bpf.h 125 | cp ~/build/pristine/linux-stable/include/uapi/linux/bpf.h include/uapi/linux/bpf.h 126 | mv kernel/bpf/bpf_lsm.c kernel/bpf/_bpf_lsm.c 127 | cp ~/build/pristine/linux-stable/kernel/bpf/bpf_lsm.c kernel/bpf/bpf_lsm.c 128 | 129 | patch: copy_change 130 | mkdir -p patches 131 | cd ~/build/pristine/linux-stable && rm -f .config 132 | cd ~/build/pristine/linux-stable && rm -f config_sav 133 | cd ~/build/pristine/linux-stable && rm -f certs/signing_key.pem 134 | cd ~/build/pristine/linux-stable && rm -f certs/x509.genkey 135 | cd ~/build/pristine/linux-stable && rm -f certs/signing_key.x509 136 | cd ~/build/pristine/linux-stable && rm -f tools/objtool/arch/x86/insn/inat-tables.c 137 | cd ~/build/pristine/linux-stable && $(MAKE) clean 138 | cd ~/build/pristine/linux-stable && $(MAKE) mrproper 139 | cp -r kernel ~/build/pristine/linux-stable/. 140 | cp -r include ~/build/pristine/linux-stable/. 141 | cd ~/build/pristine/linux-stable && git status 142 | cd ~/build/pristine/linux-stable && git add . 143 | cd ~/build/pristine/linux-stable && git commit -a -m 'provbpf' 144 | cd ~/build/pristine/linux-stable && git format-patch HEAD~ -s 145 | cp -f ~/build/pristine/linux-stable/*.patch patches/ 146 | 147 | apply_camflow: 148 | cd ~/build && wget https://github.com/camflow/camflow-dev/releases/download/v$(camflow-version)/0001-information-flow.patch 149 | cd ~/build/linux-stable && git apply ../0001-information-flow.patch 150 | cd ~/build && wget https://github.com/camflow/camflow-dev/releases/download/v$(camflow-version)/0002-camflow.patch 151 | cd ~/build/linux-stable && git apply ../0002-camflow.patch 152 | 153 | apply_bpf: 154 | cp -f ./patches/0001-provbpf.patch ~/build/0003-provbpf.patch 155 | cd ~/build/linux-stable && git apply ../0003-provbpf.patch 156 | 157 | prepare_camflow: 158 | $(MAKE) prepare 159 | $(MAKE) patch 160 | $(MAKE) delete 161 | $(MAKE) prepare 162 | $(MAKE) apply_camflow 163 | $(MAKE) apply_bpf 164 | 165 | config_camflow: 166 | cp -f /boot/config-$(shell uname -r) ~/build/linux-stable/.config 167 | cd ~/build/linux-stable && sed -i -e "s/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor\"/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf,provenance\"/g" .config 168 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_BPF_LSM is not set/CONFIG_BPF_LSM=y/g" .config 169 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_ADVANCED_ROUTER is not set/CONFIG_IP_ADVANCED_ROUTER=y/g" .config 170 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_MULTIPLE_TABLES is not set/CONFIG_IP_MULTIPLE_TABLES=y/g" .config 171 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK is not set/CONFIG_NETFILTER_NETLINK=y/g" .config 172 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_QUEUE is not set/CONFIG_NETFILTER_NETLINK_QUEUE=y/g" .config 173 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_ACCT is not set/CONFIG_NETFILTER_NETLINK_ACCT=y/g" .config 174 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_LOG is not set/CONFIG_NETFILTER_NETLINK_LOG=y/g" .config 175 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CT_NETLINK is not set/CONFIG_NF_CT_NETLINK=y/g" .config 176 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_TARGET_MARK is not set/CONFIG_NETFILTER_XT_TARGET_MARK=y/g" .config 177 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCHED is not set/CONFIG_NET_SCHED=y/g" .config 178 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCH_INGRESS is not set/CONFIG_NET_SCH_INGRESS=y/g" .config 179 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 180 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 181 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_FC_ATTRS is not set/CONFIG_SCSI_FC_ATTRS=y/g" .config 182 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI is not set/CONFIG_SCSI=y/g" .config 183 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CONNTRACK is not set/CONFIG_NF_CONNTRACK=y/g" .config 184 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_MARK is not set/CONFIG_NETFILTER_XT_MARK=y/g" .config 185 | cd ~/build/linux-stable && echo "CONFIG_NF_CT_NETLINK=y" >> .config 186 | cd ~/build/linux-stable && echo "CONFIG_SCSI_NETLINK=y" >> .config 187 | cd ~/build/linux-stable && $(MAKE) menuconfig 188 | cp -f ~/build/linux-stable/.config .config 189 | 190 | config_circle_camflow: copy_change 191 | cd ~/build/linux-stable && $(MAKE) olddefconfig 192 | cd ~/build/linux-stable && sed -i -e "s/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor\"/CONFIG_LSM=\"yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf,camflow\"/g" .config 193 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_BPF_LSM is not set/CONFIG_BPF_LSM=y/g" .config 194 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_ADVANCED_ROUTER is not set/CONFIG_IP_ADVANCED_ROUTER=y/g" .config 195 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_IP_MULTIPLE_TABLES is not set/CONFIG_IP_MULTIPLE_TABLES=y/g" .config 196 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK is not set/CONFIG_NETFILTER_NETLINK=y/g" .config 197 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_QUEUE is not set/CONFIG_NETFILTER_NETLINK_QUEUE=y/g" .config 198 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_ACCT is not set/CONFIG_NETFILTER_NETLINK_ACCT=y/g" .config 199 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_NETLINK_LOG is not set/CONFIG_NETFILTER_NETLINK_LOG=y/g" .config 200 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CT_NETLINK is not set/CONFIG_NF_CT_NETLINK=y/g" .config 201 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_TARGET_MARK is not set/CONFIG_NETFILTER_XT_TARGET_MARK=y/g" .config 202 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCHED is not set/CONFIG_NET_SCHED=y/g" .config 203 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NET_SCH_INGRESS is not set/CONFIG_NET_SCH_INGRESS=y/g" .config 204 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 205 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_NETLINK is not set/CONFIG_SCSI_NETLINK=y/g" .config 206 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI_FC_ATTRS is not set/CONFIG_SCSI_FC_ATTRS=y/g" .config 207 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_SCSI is not set/CONFIG_SCSI=y/g" .config 208 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NF_CONNTRACK is not set/CONFIG_NF_CONNTRACK=y/g" .config 209 | cd ~/build/linux-stable && sed -i -e "s/# CONFIG_NETFILTER_XT_MARK is not set/CONFIG_NETFILTER_XT_MARK=y/g" .config 210 | cd ~/build/linux-stable && echo "CONFIG_NF_CT_NETLINK=y" >> .config 211 | cd ~/build/linux-stable && echo "CONFIG_SCSI_NETLINK=y" >> .config 212 | cp -f ~/build/linux-stable/.config .config 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # provbpf-kernel 2 | 3 | ## Building and installing the kernel 4 | 5 | Preparing mainline kernel source: 6 | ``` 7 | make prepare 8 | ``` 9 | 10 | Configuring the kernel: 11 | ``` 12 | make config 13 | ``` 14 | 15 | Build the kernel: 16 | ``` 17 | make build 18 | ``` 19 | 20 | Install the kernel: 21 | ``` 22 | make install 23 | ``` 24 | 25 | ## Creating the kernel patch 26 | 27 | ``` 28 | git config --global user.name '' 29 | git config --global user.email '' 30 | make patch 31 | ``` 32 | 33 | The patch will be located in `patches/0001-provbf.patch` 34 | 35 | ## Building kernel with CamFlow patch + saBPF patch 36 | 37 | You should first [install CamFlow](https://github.com/CamFlow/camflow-install). 38 | 39 | To perform benchmarks we want to build a kernel with both this bpf and camflow patches for comparison. 40 | This is how to prepare, build and configure such setting: 41 | 42 | ``` 43 | git config --global user.name '' 44 | git config --global user.email '' 45 | make prepare_camflow 46 | make config_camflow 47 | make build 48 | make install 49 | ``` 50 | 51 | During `make config_nochange` pick the appropriate options to test the desired configuration. 52 | -------------------------------------------------------------------------------- /fedora-dependency.sh: -------------------------------------------------------------------------------- 1 | sudo dnf -y -v groupinstall 'Development Tools' 2 | sudo dnf -y -v install fedpkg fedora-packager rpmdevtools ncurses-devel 3 | sudo dnf -y -v install cmake clang gcc-c++ wget git pesign grubby 4 | sudo dnf -y -v install openssl-devel bc nano patch mosquitto sparse 5 | sudo dnf -y -v install flawfinder 6 | sudo dnf -y -v install bison flex 7 | sudo dnf -y -v install uthash-devel 8 | sudo dnf -y -v install inih-devel 9 | sudo dnf -y -v install paho-c-devel 10 | sudo dnf -y -v install uncrustify 11 | sudo dnf -y -v install rpm-build redhat-rpm-config 12 | sudo dnf -y -v install coccinelle 13 | sudo dnf -y -v install phoronix-test-suite 14 | sudo dnf -y -v install automake 15 | sudo dnf -y -v install alien 16 | sudo dnf -y -v install dwarves 17 | sudo dnf -y -v install python3.8 python3-devel 18 | sudo dnf -y -v install llvm elfutils-libelf elfutils-libelf-devel 19 | sudo dnf -y -v install libcap 20 | sudo dnf -y -v install openssl 21 | sudo dnf -y -v install valgrind 22 | sudo dnf -y -v install binutils-devel 23 | sudo dnf -y -v install readline-devel 24 | sudo dnf -y -v install libcap-devel 25 | sudo dnf -y -v install ruby 26 | sudo dnf -y -v install libbpf 27 | -------------------------------------------------------------------------------- /include/linux/bpf_local_storage.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (c) 2019 Facebook 4 | * Copyright 2020 Google LLC. 5 | */ 6 | 7 | #ifndef _BPF_LOCAL_STORAGE_H 8 | #define _BPF_LOCAL_STORAGE_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define BPF_LOCAL_STORAGE_CACHE_SIZE 16 18 | 19 | struct bpf_local_storage_map_bucket { 20 | struct hlist_head list; 21 | raw_spinlock_t lock; 22 | }; 23 | 24 | /* Thp map is not the primary owner of a bpf_local_storage_elem. 25 | * Instead, the container object (eg. sk->sk_bpf_storage) is. 26 | * 27 | * The map (bpf_local_storage_map) is for two purposes 28 | * 1. Define the size of the "local storage". It is 29 | * the map's value_size. 30 | * 31 | * 2. Maintain a list to keep track of all elems such 32 | * that they can be cleaned up during the map destruction. 33 | * 34 | * When a bpf local storage is being looked up for a 35 | * particular object, the "bpf_map" pointer is actually used 36 | * as the "key" to search in the list of elem in 37 | * the respective bpf_local_storage owned by the object. 38 | * 39 | * e.g. sk->sk_bpf_storage is the mini-map with the "bpf_map" pointer 40 | * as the searching key. 41 | */ 42 | struct bpf_local_storage_map { 43 | struct bpf_map map; 44 | /* Lookup elem does not require accessing the map. 45 | * 46 | * Updating/Deleting requires a bucket lock to 47 | * link/unlink the elem from the map. Having 48 | * multiple buckets to improve contention. 49 | */ 50 | struct bpf_local_storage_map_bucket *buckets; 51 | u32 bucket_log; 52 | u16 elem_size; 53 | u16 cache_idx; 54 | }; 55 | 56 | struct bpf_local_storage_data { 57 | /* smap is used as the searching key when looking up 58 | * from the object's bpf_local_storage. 59 | * 60 | * Put it in the same cacheline as the data to minimize 61 | * the number of cachelines access during the cache hit case. 62 | */ 63 | struct bpf_local_storage_map __rcu *smap; 64 | u8 data[] __aligned(8); 65 | }; 66 | 67 | /* Linked to bpf_local_storage and bpf_local_storage_map */ 68 | struct bpf_local_storage_elem { 69 | struct hlist_node map_node; /* Linked to bpf_local_storage_map */ 70 | struct hlist_node snode; /* Linked to bpf_local_storage */ 71 | struct bpf_local_storage __rcu *local_storage; 72 | struct rcu_head rcu; 73 | /* 8 bytes hole */ 74 | /* The data is stored in aother cacheline to minimize 75 | * the number of cachelines access during a cache hit. 76 | */ 77 | struct bpf_local_storage_data sdata ____cacheline_aligned; 78 | }; 79 | 80 | struct bpf_local_storage { 81 | struct bpf_local_storage_data __rcu *cache[BPF_LOCAL_STORAGE_CACHE_SIZE]; 82 | struct hlist_head list; /* List of bpf_local_storage_elem */ 83 | void *owner; /* The object that owns the above "list" of 84 | * bpf_local_storage_elem. 85 | */ 86 | struct rcu_head rcu; 87 | raw_spinlock_t lock; /* Protect adding/removing from the "list" */ 88 | }; 89 | 90 | /* U16_MAX is much more than enough for sk local storage 91 | * considering a tcp_sock is ~2k. 92 | */ 93 | #define BPF_LOCAL_STORAGE_MAX_VALUE_SIZE \ 94 | min_t(u32, \ 95 | (KMALLOC_MAX_SIZE - MAX_BPF_STACK - \ 96 | sizeof(struct bpf_local_storage_elem)), \ 97 | (U16_MAX - sizeof(struct bpf_local_storage_elem))) 98 | 99 | #define SELEM(_SDATA) \ 100 | container_of((_SDATA), struct bpf_local_storage_elem, sdata) 101 | #define SDATA(_SELEM) (&(_SELEM)->sdata) 102 | 103 | #define BPF_LOCAL_STORAGE_CACHE_SIZE 16 104 | 105 | struct bpf_local_storage_cache { 106 | spinlock_t idx_lock; 107 | u64 idx_usage_counts[BPF_LOCAL_STORAGE_CACHE_SIZE]; 108 | }; 109 | 110 | #define DEFINE_BPF_STORAGE_CACHE(name) \ 111 | static struct bpf_local_storage_cache name = { \ 112 | .idx_lock = __SPIN_LOCK_UNLOCKED(name.idx_lock), \ 113 | } 114 | 115 | u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache); 116 | void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache, 117 | u16 idx); 118 | 119 | /* Helper functions for bpf_local_storage */ 120 | int bpf_local_storage_map_alloc_check(union bpf_attr *attr); 121 | 122 | struct bpf_local_storage_map *bpf_local_storage_map_alloc(union bpf_attr *attr); 123 | 124 | struct bpf_local_storage_data * 125 | bpf_local_storage_lookup(struct bpf_local_storage *local_storage, 126 | struct bpf_local_storage_map *smap, 127 | bool cacheit_lockit); 128 | 129 | void bpf_local_storage_map_free(struct bpf_local_storage_map *smap); 130 | 131 | int bpf_local_storage_map_check_btf(const struct bpf_map *map, 132 | const struct btf *btf, 133 | const struct btf_type *key_type, 134 | const struct btf_type *value_type); 135 | 136 | void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage, 137 | struct bpf_local_storage_elem *selem); 138 | 139 | bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage, 140 | struct bpf_local_storage_elem *selem, 141 | bool uncharge_omem); 142 | 143 | void bpf_selem_unlink(struct bpf_local_storage_elem *selem); 144 | 145 | void bpf_selem_link_map(struct bpf_local_storage_map *smap, 146 | struct bpf_local_storage_elem *selem); 147 | 148 | void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem); 149 | 150 | struct bpf_local_storage_elem * 151 | bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, void *value, 152 | bool charge_mem); 153 | 154 | int 155 | bpf_local_storage_alloc(void *owner, 156 | struct bpf_local_storage_map *smap, 157 | struct bpf_local_storage_elem *first_selem); 158 | 159 | struct bpf_local_storage_data * 160 | bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, 161 | void *value, u64 map_flags); 162 | 163 | #endif /* _BPF_LOCAL_STORAGE_H */ 164 | -------------------------------------------------------------------------------- /include/linux/bpf_lsm.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | 3 | /* 4 | * Copyright (C) 2020 Google LLC. 5 | */ 6 | 7 | #ifndef _LINUX_BPF_LSM_H 8 | #define _LINUX_BPF_LSM_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef CONFIG_BPF_LSM 17 | 18 | #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ 19 | RET bpf_lsm_##NAME(__VA_ARGS__); 20 | #include 21 | #undef LSM_HOOK 22 | 23 | struct bpf_storage_blob { 24 | struct bpf_local_storage __rcu *storage; 25 | }; 26 | 27 | extern struct lsm_blob_sizes bpf_lsm_blob_sizes; 28 | 29 | int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, 30 | const struct bpf_prog *prog); 31 | 32 | bool bpf_lsm_is_sleepable_hook(u32 btf_id); 33 | 34 | static inline struct bpf_storage_blob *bpf_inode( 35 | const struct inode *inode) 36 | { 37 | if (unlikely(!inode->i_security)) 38 | return NULL; 39 | 40 | return inode->i_security + bpf_lsm_blob_sizes.lbs_inode; 41 | } 42 | 43 | static inline struct bpf_storage_blob *bpf_task( 44 | const struct task_struct *task) 45 | { 46 | if (unlikely(!task->security)) 47 | return NULL; 48 | 49 | return task->security + bpf_lsm_blob_sizes.lbs_task; 50 | } 51 | 52 | /* systopia contrib start */ 53 | static inline struct bpf_storage_blob *bpf_cred( 54 | const struct cred *cred) 55 | { 56 | if (unlikely(!cred->security)) 57 | return NULL; 58 | 59 | return cred->security + bpf_lsm_blob_sizes.lbs_cred; 60 | } 61 | 62 | static inline struct bpf_storage_blob *bpf_msg( 63 | const struct msg_msg *msg) 64 | { 65 | if (unlikely(!msg->security)) 66 | return NULL; 67 | 68 | return msg->security + bpf_lsm_blob_sizes.lbs_msg_msg; 69 | } 70 | 71 | static inline struct bpf_storage_blob *bpf_ipc( 72 | const struct kern_ipc_perm *ipc) 73 | { 74 | if (unlikely(!ipc->security)) 75 | return NULL; 76 | 77 | return ipc->security + bpf_lsm_blob_sizes.lbs_ipc; 78 | } 79 | 80 | static inline struct bpf_storage_blob *bpf_file( 81 | const struct file *file) 82 | { 83 | if (unlikely(!file->f_security)) 84 | return NULL; 85 | 86 | return file->f_security + bpf_lsm_blob_sizes.lbs_file; 87 | } 88 | /* systopia contrib end */ 89 | 90 | extern const struct bpf_func_proto bpf_inode_storage_get_proto; 91 | extern const struct bpf_func_proto bpf_inode_storage_delete_proto; 92 | extern const struct bpf_func_proto bpf_task_storage_get_proto; 93 | extern const struct bpf_func_proto bpf_task_storage_delete_proto; 94 | /* systopia contrib start */ 95 | extern const struct bpf_func_proto bpf_cred_storage_get_proto; 96 | extern const struct bpf_func_proto bpf_cred_storage_delete_proto; 97 | extern const struct bpf_func_proto bpf_msg_storage_get_proto; 98 | extern const struct bpf_func_proto bpf_msg_storage_delete_proto; 99 | extern const struct bpf_func_proto bpf_ipc_storage_get_proto; 100 | extern const struct bpf_func_proto bpf_ipc_storage_delete_proto; 101 | extern const struct bpf_func_proto bpf_file_storage_get_proto; 102 | extern const struct bpf_func_proto bpf_file_storage_delete_proto; 103 | /* systopia contrib end */ 104 | void bpf_inode_storage_free(struct inode *inode); 105 | void bpf_task_storage_free(struct task_struct *task); 106 | /* systopia contrib start */ 107 | void bpf_cred_storage_free(struct cred *cred); 108 | void bpf_msg_storage_free(struct msg_msg *msg); 109 | void bpf_ipc_storage_free(struct kern_ipc_perm *ipc); 110 | void bpf_file_storage_free(struct file *file); 111 | /* systopia contrib end */ 112 | 113 | #else /* !CONFIG_BPF_LSM */ 114 | 115 | static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) 116 | { 117 | return false; 118 | } 119 | 120 | static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, 121 | const struct bpf_prog *prog) 122 | { 123 | return -EOPNOTSUPP; 124 | } 125 | 126 | static inline struct bpf_storage_blob *bpf_inode( 127 | const struct inode *inode) 128 | { 129 | return NULL; 130 | } 131 | 132 | static inline struct bpf_storage_blob *bpf_task( 133 | const struct task_struct *task) 134 | { 135 | return NULL; 136 | } 137 | 138 | /* systopia contrib start */ 139 | static inline struct bpf_storage_blob *bpf_cred( 140 | const struct cred *cred) 141 | { 142 | return NULL; 143 | } 144 | 145 | static inline struct bpf_storage_blob *bpf_msg( 146 | const struct msg_msg *msg) 147 | { 148 | return NULL; 149 | } 150 | 151 | static inline struct bpf_storage_blob *bpf_ipc( 152 | const struct kern_ipc_perm *ipc) 153 | { 154 | return NULL; 155 | } 156 | 157 | static inline struct bpf_storage_blob *bpf_file( 158 | const struct file *file) 159 | { 160 | return NULL; 161 | } 162 | /* systopia contrib end */ 163 | 164 | static inline void bpf_inode_storage_free(struct inode *inode) 165 | { 166 | } 167 | 168 | static inline void bpf_task_storage_free(struct task_struct *task) 169 | { 170 | } 171 | 172 | /* systopia contrib start */ 173 | static inline void bpf_cred_storage_free(struct cred *cred) 174 | { 175 | } 176 | 177 | static inline void bpf_msg_storage_free(struct msg_msg *msg) 178 | { 179 | } 180 | 181 | static inline void bpf_ipc_storage_free(struct kern_ipc_perm *ipc) 182 | { 183 | } 184 | 185 | static inline void bpf_file_storage_free(struct file *file) 186 | { 187 | } 188 | /* systopia contrib end */ 189 | 190 | #endif /* CONFIG_BPF_LSM */ 191 | 192 | #endif /* _LINUX_BPF_LSM_H */ 193 | -------------------------------------------------------------------------------- /include/linux/bpf_types.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* internal file - do not include directly */ 3 | 4 | #ifdef CONFIG_NET 5 | BPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter, 6 | struct __sk_buff, struct sk_buff) 7 | BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act, 8 | struct __sk_buff, struct sk_buff) 9 | BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act, 10 | struct __sk_buff, struct sk_buff) 11 | BPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp, 12 | struct xdp_md, struct xdp_buff) 13 | #ifdef CONFIG_CGROUP_BPF 14 | BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb, 15 | struct __sk_buff, struct sk_buff) 16 | BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock, 17 | struct bpf_sock, struct sock) 18 | BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, cg_sock_addr, 19 | struct bpf_sock_addr, struct bpf_sock_addr_kern) 20 | #endif 21 | BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_in, 22 | struct __sk_buff, struct sk_buff) 23 | BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_out, 24 | struct __sk_buff, struct sk_buff) 25 | BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit, 26 | struct __sk_buff, struct sk_buff) 27 | BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_SEG6LOCAL, lwt_seg6local, 28 | struct __sk_buff, struct sk_buff) 29 | BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops, 30 | struct bpf_sock_ops, struct bpf_sock_ops_kern) 31 | BPF_PROG_TYPE(BPF_PROG_TYPE_SK_SKB, sk_skb, 32 | struct __sk_buff, struct sk_buff) 33 | BPF_PROG_TYPE(BPF_PROG_TYPE_SK_MSG, sk_msg, 34 | struct sk_msg_md, struct sk_msg) 35 | BPF_PROG_TYPE(BPF_PROG_TYPE_FLOW_DISSECTOR, flow_dissector, 36 | struct __sk_buff, struct bpf_flow_dissector) 37 | #endif 38 | #ifdef CONFIG_BPF_EVENTS 39 | BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe, 40 | bpf_user_pt_regs_t, struct pt_regs) 41 | BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint, 42 | __u64, u64) 43 | BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event, 44 | struct bpf_perf_event_data, struct bpf_perf_event_data_kern) 45 | BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint, 46 | struct bpf_raw_tracepoint_args, u64) 47 | BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, raw_tracepoint_writable, 48 | struct bpf_raw_tracepoint_args, u64) 49 | BPF_PROG_TYPE(BPF_PROG_TYPE_TRACING, tracing, 50 | void *, void *) 51 | #endif 52 | #ifdef CONFIG_CGROUP_BPF 53 | BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev, 54 | struct bpf_cgroup_dev_ctx, struct bpf_cgroup_dev_ctx) 55 | BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SYSCTL, cg_sysctl, 56 | struct bpf_sysctl, struct bpf_sysctl_kern) 57 | BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCKOPT, cg_sockopt, 58 | struct bpf_sockopt, struct bpf_sockopt_kern) 59 | #endif 60 | #ifdef CONFIG_BPF_LIRC_MODE2 61 | BPF_PROG_TYPE(BPF_PROG_TYPE_LIRC_MODE2, lirc_mode2, 62 | __u32, u32) 63 | #endif 64 | #ifdef CONFIG_INET 65 | BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport, 66 | struct sk_reuseport_md, struct sk_reuseport_kern) 67 | BPF_PROG_TYPE(BPF_PROG_TYPE_SK_LOOKUP, sk_lookup, 68 | struct bpf_sk_lookup, struct bpf_sk_lookup_kern) 69 | #endif 70 | #if defined(CONFIG_BPF_JIT) 71 | BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops, 72 | void *, void *) 73 | BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension, 74 | void *, void *) 75 | #ifdef CONFIG_BPF_LSM 76 | BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm, 77 | void *, void *) 78 | #endif /* CONFIG_BPF_LSM */ 79 | #endif 80 | 81 | BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) 82 | BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops) 83 | BPF_MAP_TYPE(BPF_MAP_TYPE_PROG_ARRAY, prog_array_map_ops) 84 | BPF_MAP_TYPE(BPF_MAP_TYPE_PERF_EVENT_ARRAY, perf_event_array_map_ops) 85 | #ifdef CONFIG_CGROUPS 86 | BPF_MAP_TYPE(BPF_MAP_TYPE_CGROUP_ARRAY, cgroup_array_map_ops) 87 | #endif 88 | #ifdef CONFIG_CGROUP_BPF 89 | BPF_MAP_TYPE(BPF_MAP_TYPE_CGROUP_STORAGE, cgroup_storage_map_ops) 90 | BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, cgroup_storage_map_ops) 91 | #endif 92 | BPF_MAP_TYPE(BPF_MAP_TYPE_HASH, htab_map_ops) 93 | BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_HASH, htab_percpu_map_ops) 94 | BPF_MAP_TYPE(BPF_MAP_TYPE_LRU_HASH, htab_lru_map_ops) 95 | BPF_MAP_TYPE(BPF_MAP_TYPE_LRU_PERCPU_HASH, htab_lru_percpu_map_ops) 96 | BPF_MAP_TYPE(BPF_MAP_TYPE_LPM_TRIE, trie_map_ops) 97 | #ifdef CONFIG_PERF_EVENTS 98 | BPF_MAP_TYPE(BPF_MAP_TYPE_STACK_TRACE, stack_trace_map_ops) 99 | #endif 100 | BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY_OF_MAPS, array_of_maps_map_ops) 101 | BPF_MAP_TYPE(BPF_MAP_TYPE_HASH_OF_MAPS, htab_of_maps_map_ops) 102 | #ifdef CONFIG_NET 103 | BPF_MAP_TYPE(BPF_MAP_TYPE_DEVMAP, dev_map_ops) 104 | BPF_MAP_TYPE(BPF_MAP_TYPE_DEVMAP_HASH, dev_map_hash_ops) 105 | BPF_MAP_TYPE(BPF_MAP_TYPE_SK_STORAGE, sk_storage_map_ops) 106 | #if defined(CONFIG_BPF_STREAM_PARSER) 107 | BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKMAP, sock_map_ops) 108 | BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops) 109 | #endif 110 | #ifdef CONFIG_BPF_LSM 111 | BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops) 112 | BPF_MAP_TYPE(BPF_MAP_TYPE_TASK_STORAGE, task_storage_map_ops) 113 | /* systopia contrib start */ 114 | BPF_MAP_TYPE(BPF_MAP_TYPE_CRED_STORAGE, cred_storage_map_ops) 115 | BPF_MAP_TYPE(BPF_MAP_TYPE_MSG_STORAGE, msg_storage_map_ops) 116 | BPF_MAP_TYPE(BPF_MAP_TYPE_IPC_STORAGE, ipc_storage_map_ops) 117 | BPF_MAP_TYPE(BPF_MAP_TYPE_FILE_STORAGE, file_storage_map_ops) 118 | /* systopia contrib end */ 119 | #endif 120 | BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops) 121 | #if defined(CONFIG_XDP_SOCKETS) 122 | BPF_MAP_TYPE(BPF_MAP_TYPE_XSKMAP, xsk_map_ops) 123 | #endif 124 | #ifdef CONFIG_INET 125 | BPF_MAP_TYPE(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, reuseport_array_ops) 126 | #endif 127 | #endif 128 | BPF_MAP_TYPE(BPF_MAP_TYPE_QUEUE, queue_map_ops) 129 | BPF_MAP_TYPE(BPF_MAP_TYPE_STACK, stack_map_ops) 130 | #if defined(CONFIG_BPF_JIT) 131 | BPF_MAP_TYPE(BPF_MAP_TYPE_STRUCT_OPS, bpf_struct_ops_map_ops) 132 | #endif 133 | BPF_MAP_TYPE(BPF_MAP_TYPE_RINGBUF, ringbuf_map_ops) 134 | 135 | BPF_LINK_TYPE(BPF_LINK_TYPE_RAW_TRACEPOINT, raw_tracepoint) 136 | BPF_LINK_TYPE(BPF_LINK_TYPE_TRACING, tracing) 137 | #ifdef CONFIG_CGROUP_BPF 138 | BPF_LINK_TYPE(BPF_LINK_TYPE_CGROUP, cgroup) 139 | #endif 140 | BPF_LINK_TYPE(BPF_LINK_TYPE_ITER, iter) 141 | #ifdef CONFIG_NET 142 | BPF_LINK_TYPE(BPF_LINK_TYPE_NETNS, netns) 143 | #endif 144 | -------------------------------------------------------------------------------- /kernel/bpf/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | obj-y := core.o 3 | ifneq ($(CONFIG_BPF_JIT_ALWAYS_ON),y) 4 | # ___bpf_prog_run() needs GCSE disabled on x86; see 3193c0836f203 for details 5 | cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse 6 | endif 7 | CFLAGS_core.o += $(call cc-disable-warning, override-init) $(cflags-nogcse-yy) 8 | 9 | obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o prog_iter.o 10 | obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o 11 | obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o 12 | obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o 13 | obj-${CONFIG_BPF_LSM} += bpf_task_storage.o 14 | # systopia contrib start # 15 | obj-${CONFIG_BPF_LSM} += bpf_cred_storage.o 16 | obj-${CONFIG_BPF_LSM} += bpf_msg_storage.o 17 | obj-${CONFIG_BPF_LSM} += bpf_ipc_storage.o 18 | obj-${CONFIG_BPF_LSM} += bpf_file_storage.o 19 | # systopia contrib end # 20 | obj-$(CONFIG_BPF_SYSCALL) += disasm.o 21 | obj-$(CONFIG_BPF_JIT) += trampoline.o 22 | obj-$(CONFIG_BPF_SYSCALL) += btf.o 23 | obj-$(CONFIG_BPF_JIT) += dispatcher.o 24 | ifeq ($(CONFIG_NET),y) 25 | obj-$(CONFIG_BPF_SYSCALL) += devmap.o 26 | obj-$(CONFIG_BPF_SYSCALL) += cpumap.o 27 | obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o 28 | obj-$(CONFIG_BPF_SYSCALL) += offload.o 29 | obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o 30 | endif 31 | ifeq ($(CONFIG_PERF_EVENTS),y) 32 | obj-$(CONFIG_BPF_SYSCALL) += stackmap.o 33 | endif 34 | obj-$(CONFIG_CGROUP_BPF) += cgroup.o 35 | ifeq ($(CONFIG_INET),y) 36 | obj-$(CONFIG_BPF_SYSCALL) += reuseport_array.o 37 | endif 38 | ifeq ($(CONFIG_SYSFS),y) 39 | obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o 40 | endif 41 | ifeq ($(CONFIG_BPF_JIT),y) 42 | obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o 43 | obj-${CONFIG_BPF_LSM} += bpf_lsm.o 44 | endif 45 | obj-$(CONFIG_BPF_PRELOAD) += preload/ 46 | -------------------------------------------------------------------------------- /kernel/bpf/bpf_cred_storage.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2020-2021 Harvard University 4 | * Copyright (C) 2020-2021 University of Bristol 5 | * 6 | * Author: Thomas Pasquier 7 | * Author: Bogdan Stelea 8 | * Author: Soo Yee Lim 9 | * Author: Xueyuan "Michael" Han 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License version 2, as 13 | * published by the Free Software Foundation; either version 2 of the License, 14 | * or (at your option) any later version. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | DEFINE_BPF_STORAGE_CACHE(cred_cache); 34 | 35 | static struct bpf_local_storage __rcu **cred_storage_ptr(void *owner) 36 | { 37 | struct cred *cred = owner; 38 | struct bpf_storage_blob *bsb; 39 | 40 | bsb = bpf_cred(cred); 41 | if (!bsb) 42 | return NULL; 43 | return &bsb->storage; 44 | } 45 | 46 | static struct bpf_local_storage_data * 47 | cred_storage_lookup(struct cred *cred, struct bpf_map *map, bool cacheit_lockit) 48 | { 49 | struct bpf_local_storage *cred_storage; 50 | struct bpf_local_storage_map *smap; 51 | struct bpf_storage_blob *bsb; 52 | 53 | bsb = bpf_cred(cred); 54 | if (!bsb) 55 | return NULL; 56 | 57 | cred_storage = rcu_dereference(bsb->storage); 58 | if (!cred_storage) 59 | return NULL; 60 | 61 | smap = (struct bpf_local_storage_map *)map; 62 | return bpf_local_storage_lookup(cred_storage, smap, cacheit_lockit); 63 | } 64 | 65 | void bpf_cred_storage_free(struct cred *cred) 66 | { 67 | struct bpf_local_storage_elem *selem; 68 | struct bpf_local_storage *local_storage; 69 | bool free_cred_storage = false; 70 | struct bpf_storage_blob *bsb; 71 | struct hlist_node *n; 72 | 73 | bsb = bpf_cred(cred); 74 | if (!bsb) 75 | return; 76 | 77 | rcu_read_lock(); 78 | 79 | local_storage = rcu_dereference(bsb->storage); 80 | if (!local_storage) { 81 | rcu_read_unlock(); 82 | return; 83 | } 84 | 85 | /* Neither the bpf_prog nor the bpf-map's syscall 86 | * could be modifying the local_storage->list now. 87 | * Thus, no elem can be added-to or deleted-from the 88 | * local_storage->list by the bpf_prog or by the bpf-map's syscall. 89 | * 90 | * It is racing with bpf_local_storage_map_free() alone 91 | * when unlinking elem from the local_storage->list and 92 | * the map's bucket->list. 93 | */ 94 | raw_spin_lock_bh(&local_storage->lock); 95 | hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { 96 | /* Always unlink from map before unlinking from 97 | * local_storage. 98 | */ 99 | bpf_selem_unlink_map(selem); 100 | free_cred_storage = bpf_selem_unlink_storage_nolock( 101 | local_storage, selem, false); 102 | } 103 | raw_spin_unlock_bh(&local_storage->lock); 104 | rcu_read_unlock(); 105 | 106 | /* free_cred_storage should always be true as long as 107 | * local_storage->list was non-empty. 108 | */ 109 | if (free_cred_storage) 110 | kfree_rcu(local_storage, rcu); 111 | } 112 | 113 | static void *bpf_pid_cred_storage_lookup_elem(struct bpf_map *map, void *key) 114 | { 115 | struct bpf_local_storage_data *sdata; 116 | struct task_struct *task; 117 | unsigned int f_flags; 118 | struct pid *pid; 119 | int fd, err; 120 | 121 | fd = *(int *)key; 122 | pid = pidfd_get_pid(fd, &f_flags); 123 | if (IS_ERR(pid)) 124 | return ERR_CAST(pid); 125 | 126 | /* We should be in an RCU read side critical section, it should be safe 127 | * to call pid_task. 128 | */ 129 | WARN_ON_ONCE(!rcu_read_lock_held()); 130 | task = pid_task(pid, PIDTYPE_PID); 131 | if (!task || !task->cred) { 132 | err = -ENOENT; 133 | goto out; 134 | } 135 | 136 | sdata = cred_storage_lookup((struct cred *)task->cred, map, true); 137 | put_pid(pid); 138 | return sdata ? sdata->data : NULL; 139 | out: 140 | put_pid(pid); 141 | return ERR_PTR(err); 142 | } 143 | 144 | 145 | static int bpf_pid_cred_storage_update_elem(struct bpf_map *map, void *key, 146 | void *value, u64 map_flags) 147 | { 148 | struct bpf_local_storage_data *sdata; 149 | struct task_struct *task; 150 | unsigned int f_flags; 151 | struct pid *pid; 152 | int fd, err; 153 | 154 | fd = *(int *)key; 155 | pid = pidfd_get_pid(fd, &f_flags); 156 | if (IS_ERR(pid)) 157 | return PTR_ERR(pid); 158 | 159 | /* We should be in an RCU read side critical section, it should be safe 160 | * to call pid_task. 161 | */ 162 | WARN_ON_ONCE(!rcu_read_lock_held()); 163 | task = pid_task(pid, PIDTYPE_PID); 164 | if (!task || !task->cred || !cred_storage_ptr((struct cred *)task->cred)) { 165 | err = -ENOENT; 166 | goto out; 167 | } 168 | 169 | sdata = bpf_local_storage_update( 170 | (struct cred *)task->cred, (struct bpf_local_storage_map *)map, value, map_flags); 171 | 172 | err = PTR_ERR_OR_ZERO(sdata); 173 | out: 174 | put_pid(pid); 175 | return err; 176 | } 177 | 178 | static int cred_storage_delete(struct cred *cred, struct bpf_map *map) 179 | { 180 | struct bpf_local_storage_data *sdata; 181 | 182 | sdata = cred_storage_lookup(cred, map, false); 183 | if (!sdata) 184 | return -ENOENT; 185 | 186 | bpf_selem_unlink(SELEM(sdata)); 187 | 188 | return 0; 189 | } 190 | 191 | static int bpf_pid_cred_storage_delete_elem(struct bpf_map *map, void *key) 192 | { 193 | struct task_struct *task; 194 | unsigned int f_flags; 195 | struct pid *pid; 196 | int fd, err; 197 | 198 | fd = *(int *)key; 199 | pid = pidfd_get_pid(fd, &f_flags); 200 | if (IS_ERR(pid)) 201 | return PTR_ERR(pid); 202 | 203 | /* We should be in an RCU read side critical section, it should be safe 204 | * to call pid_task. 205 | */ 206 | WARN_ON_ONCE(!rcu_read_lock_held()); 207 | task = pid_task(pid, PIDTYPE_PID); 208 | if (!task || !task->cred) { 209 | err = -ENOENT; 210 | goto out; 211 | } 212 | 213 | err = cred_storage_delete((struct cred *)task->cred, map); 214 | out: 215 | put_pid(pid); 216 | return err; 217 | } 218 | 219 | BPF_CALL_4(bpf_cred_storage_get, struct bpf_map *, map, struct cred *, 220 | cred, void *, value, u64, flags) 221 | { 222 | struct bpf_local_storage_data *sdata; 223 | 224 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) 225 | return (unsigned long)NULL; 226 | 227 | /* explicitly check that the cred_storage_ptr is not 228 | * NULL as cred_storage_lookup returns NULL in this case and 229 | * bpf_local_storage_update expects the owner to have a 230 | * valid storage pointer. 231 | */ 232 | if (!cred || !cred_storage_ptr(cred)) 233 | return (unsigned long)NULL; 234 | 235 | sdata = cred_storage_lookup(cred, map, true); 236 | if (sdata) 237 | return (unsigned long)sdata->data; 238 | 239 | /* This helper must only be called from places where the lifetime of the cred 240 | * is guaranteed. Either by being refcounted or by being protected 241 | * by an RCU read-side critical section. 242 | */ 243 | if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { 244 | sdata = bpf_local_storage_update( 245 | cred, (struct bpf_local_storage_map *)map, value, 246 | BPF_NOEXIST); 247 | return IS_ERR(sdata) ? (unsigned long)NULL : 248 | (unsigned long)sdata->data; 249 | } 250 | 251 | return (unsigned long)NULL; 252 | } 253 | 254 | BPF_CALL_2(bpf_cred_storage_delete, struct bpf_map *, map, struct cred *, cred) 255 | { 256 | if (!cred) 257 | return -EINVAL; 258 | 259 | /* This helper must only be called from places where the lifetime of the cred 260 | * is guaranteed. Either by being refcounted or by being protected 261 | * by an RCU read-side critical section. 262 | */ 263 | return cred_storage_delete(cred, map); 264 | } 265 | 266 | static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) 267 | { 268 | return -ENOTSUPP; 269 | } 270 | 271 | static struct bpf_map *cred_storage_map_alloc(union bpf_attr *attr) 272 | { 273 | struct bpf_local_storage_map *smap; 274 | 275 | smap = bpf_local_storage_map_alloc(attr); 276 | if (IS_ERR(smap)) 277 | return ERR_CAST(smap); 278 | 279 | smap->cache_idx = bpf_local_storage_cache_idx_get(&cred_cache); 280 | return &smap->map; 281 | } 282 | 283 | static void cred_storage_map_free(struct bpf_map *map) 284 | { 285 | struct bpf_local_storage_map *smap; 286 | 287 | smap = (struct bpf_local_storage_map *)map; 288 | bpf_local_storage_cache_idx_free(&cred_cache, smap->cache_idx); 289 | bpf_local_storage_map_free(smap); 290 | } 291 | 292 | static int cred_storage_map_btf_id; 293 | const struct bpf_map_ops cred_storage_map_ops = { 294 | .map_meta_equal = bpf_map_meta_equal, 295 | .map_alloc_check = bpf_local_storage_map_alloc_check, 296 | .map_alloc = cred_storage_map_alloc, 297 | .map_free = cred_storage_map_free, 298 | .map_get_next_key = notsupp_get_next_key, 299 | .map_lookup_elem = bpf_pid_cred_storage_lookup_elem, 300 | .map_update_elem = bpf_pid_cred_storage_update_elem, 301 | .map_delete_elem = bpf_pid_cred_storage_delete_elem, 302 | .map_check_btf = bpf_local_storage_map_check_btf, 303 | .map_btf_name = "bpf_local_storage_map", 304 | .map_btf_id = &cred_storage_map_btf_id, 305 | .map_owner_storage_ptr = cred_storage_ptr, 306 | }; 307 | 308 | BTF_ID_LIST_SINGLE(bpf_cred_storage_btf_ids, struct, cred) 309 | 310 | const struct bpf_func_proto bpf_cred_storage_get_proto = { 311 | .func = bpf_cred_storage_get, 312 | .gpl_only = false, 313 | .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, 314 | .arg1_type = ARG_CONST_MAP_PTR, 315 | .arg2_type = ARG_PTR_TO_BTF_ID, 316 | .arg2_btf_id = &bpf_cred_storage_btf_ids[0], 317 | .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, 318 | .arg4_type = ARG_ANYTHING, 319 | }; 320 | 321 | const struct bpf_func_proto bpf_cred_storage_delete_proto = { 322 | .func = bpf_cred_storage_delete, 323 | .gpl_only = false, 324 | .ret_type = RET_INTEGER, 325 | .arg1_type = ARG_CONST_MAP_PTR, 326 | .arg2_type = ARG_PTR_TO_BTF_ID, 327 | .arg2_btf_id = &bpf_cred_storage_btf_ids[0], 328 | }; 329 | -------------------------------------------------------------------------------- /kernel/bpf/bpf_file_storage.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2020-2021 Harvard University 4 | * Copyright (C) 2020-2021 University of Bristol 5 | * 6 | * Author: Thomas Pasquier 7 | * Author: Bogdan Stelea 8 | * Author: Soo Yee Lim 9 | * Author: Xueyuan "Michael" Han 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License version 2, as 13 | * published by the Free Software Foundation; either version 2 of the License, 14 | * or (at your option) any later version. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | DEFINE_BPF_STORAGE_CACHE(file_cache); 34 | 35 | static struct bpf_local_storage __rcu **file_storage_ptr(void *owner) 36 | { 37 | struct file *file = owner; 38 | struct bpf_storage_blob *bsb; 39 | 40 | bsb = bpf_file(file); 41 | if (!bsb) 42 | return NULL; 43 | return &bsb->storage; 44 | } 45 | 46 | static struct bpf_local_storage_data * 47 | file_storage_lookup(struct file *file, struct bpf_map *map, bool cacheit_lockit) 48 | { 49 | struct bpf_local_storage *file_storage; 50 | struct bpf_local_storage_map *smap; 51 | struct bpf_storage_blob *bsb; 52 | 53 | bsb = bpf_file(file); 54 | if (!bsb) 55 | return NULL; 56 | 57 | file_storage = rcu_dereference(bsb->storage); 58 | if (!file_storage) 59 | return NULL; 60 | 61 | smap = (struct bpf_local_storage_map *)map; 62 | return bpf_local_storage_lookup(file_storage, smap, cacheit_lockit); 63 | } 64 | 65 | void bpf_file_storage_free(struct file *file) 66 | { 67 | struct bpf_local_storage_elem *selem; 68 | struct bpf_local_storage *local_storage; 69 | bool free_file_storage = false; 70 | struct bpf_storage_blob *bsb; 71 | struct hlist_node *n; 72 | 73 | bsb = bpf_file(file); 74 | if (!bsb) 75 | return; 76 | 77 | rcu_read_lock(); 78 | 79 | local_storage = rcu_dereference(bsb->storage); 80 | if (!local_storage) { 81 | rcu_read_unlock(); 82 | return; 83 | } 84 | 85 | /* Neither the bpf_prog nor the bpf-map's syscall 86 | * could be modifying the local_storage->list now. 87 | * Thus, no elem can be added-to or deleted-from the 88 | * local_storage->list by the bpf_prog or by the bpf-map's syscall. 89 | * 90 | * It is racing with bpf_local_storage_map_free() alone 91 | * when unlinking elem from the local_storage->list and 92 | * the map's bucket->list. 93 | */ 94 | raw_spin_lock_bh(&local_storage->lock); 95 | hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { 96 | /* Always unlink from map before unlinking from 97 | * local_storage. 98 | */ 99 | bpf_selem_unlink_map(selem); 100 | free_file_storage = bpf_selem_unlink_storage_nolock( 101 | local_storage, selem, false); 102 | } 103 | raw_spin_unlock_bh(&local_storage->lock); 104 | rcu_read_unlock(); 105 | 106 | /* free_file_storage should always be true as long as 107 | * local_storage->list was non-empty. 108 | */ 109 | if (free_file_storage) 110 | kfree_rcu(local_storage, rcu); 111 | } 112 | 113 | 114 | static void *bpf_file_storage_lookup_elem(struct bpf_map *map, void *key) 115 | { 116 | return -ENOTSUPP; 117 | } 118 | 119 | 120 | static int bpf_file_storage_update_elem(struct bpf_map *map, void *key, 121 | void *value, u64 map_flags) 122 | { 123 | return -ENOTSUPP; 124 | } 125 | 126 | 127 | static int file_storage_delete(struct file *file, struct bpf_map *map) 128 | { 129 | struct bpf_local_storage_data *sdata; 130 | 131 | sdata = file_storage_lookup(file, map, false); 132 | if (!sdata) 133 | return -ENOENT; 134 | 135 | bpf_selem_unlink(SELEM(sdata)); 136 | 137 | return 0; 138 | } 139 | 140 | 141 | static int bpf_file_storage_delete_elem(struct bpf_map *map, void *key) 142 | { 143 | return -ENOTSUPP; 144 | } 145 | 146 | BPF_CALL_4(bpf_file_storage_get, struct bpf_map *, map, struct file *, 147 | file, void *, value, u64, flags) 148 | { 149 | struct bpf_local_storage_data *sdata; 150 | 151 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) 152 | return (unsigned long)NULL; 153 | 154 | /* explicitly check that the file_storage_ptr is not 155 | * NULL as file_storage_lookup returns NULL in this case and 156 | * bpf_local_storage_update expects the owner to have a 157 | * valid storage pointer. 158 | */ 159 | if (!file || !file_storage_ptr(file)) 160 | return (unsigned long)NULL; 161 | 162 | sdata = file_storage_lookup(file, map, true); 163 | if (sdata) 164 | return (unsigned long)sdata->data; 165 | 166 | /* This helper must only be called from places where the lifetime of the file 167 | * is guaranteed. Either by being refcounted or by being protected 168 | * by an RCU read-side critical section. 169 | */ 170 | if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { 171 | sdata = bpf_local_storage_update( 172 | file, (struct bpf_local_storage_map *)map, value, 173 | BPF_NOEXIST); 174 | return IS_ERR(sdata) ? (unsigned long)NULL : 175 | (unsigned long)sdata->data; 176 | } 177 | 178 | return (unsigned long)NULL; 179 | } 180 | 181 | BPF_CALL_2(bpf_file_storage_delete, struct bpf_map *, map, struct file *, file) 182 | { 183 | if (!file) 184 | return -EINVAL; 185 | 186 | /* This helper must only be called from places where the lifetime of the file 187 | * is guaranteed. Either by being refcounted or by being protected 188 | * by an RCU read-side critical section. 189 | */ 190 | return file_storage_delete(file, map); 191 | } 192 | 193 | static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) 194 | { 195 | return -ENOTSUPP; 196 | } 197 | 198 | static struct bpf_map *file_storage_map_alloc(union bpf_attr *attr) 199 | { 200 | struct bpf_local_storage_map *smap; 201 | 202 | smap = bpf_local_storage_map_alloc(attr); 203 | if (IS_ERR(smap)) 204 | return ERR_CAST(smap); 205 | 206 | smap->cache_idx = bpf_local_storage_cache_idx_get(&file_cache); 207 | return &smap->map; 208 | } 209 | 210 | static void file_storage_map_free(struct bpf_map *map) 211 | { 212 | struct bpf_local_storage_map *smap; 213 | 214 | smap = (struct bpf_local_storage_map *)map; 215 | bpf_local_storage_cache_idx_free(&file_cache, smap->cache_idx); 216 | bpf_local_storage_map_free(smap); 217 | } 218 | 219 | static int file_storage_map_btf_id; 220 | const struct bpf_map_ops file_storage_map_ops = { 221 | .map_meta_equal = bpf_map_meta_equal, 222 | .map_alloc_check = bpf_local_storage_map_alloc_check, 223 | .map_alloc = file_storage_map_alloc, 224 | .map_free = file_storage_map_free, 225 | .map_get_next_key = notsupp_get_next_key, 226 | .map_lookup_elem = bpf_file_storage_lookup_elem, 227 | .map_update_elem = bpf_file_storage_update_elem, 228 | .map_delete_elem = bpf_file_storage_delete_elem, 229 | .map_check_btf = bpf_local_storage_map_check_btf, 230 | .map_btf_name = "bpf_local_storage_map", 231 | .map_btf_id = &file_storage_map_btf_id, 232 | .map_owner_storage_ptr = file_storage_ptr, 233 | }; 234 | 235 | BTF_ID_LIST_SINGLE(bpf_file_storage_btf_ids, struct, file) 236 | 237 | const struct bpf_func_proto bpf_file_storage_get_proto = { 238 | .func = bpf_file_storage_get, 239 | .gpl_only = false, 240 | .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, 241 | .arg1_type = ARG_CONST_MAP_PTR, 242 | .arg2_type = ARG_PTR_TO_BTF_ID, 243 | .arg2_btf_id = &bpf_file_storage_btf_ids[0], 244 | .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, 245 | .arg4_type = ARG_ANYTHING, 246 | }; 247 | 248 | const struct bpf_func_proto bpf_file_storage_delete_proto = { 249 | .func = bpf_file_storage_delete, 250 | .gpl_only = false, 251 | .ret_type = RET_INTEGER, 252 | .arg1_type = ARG_CONST_MAP_PTR, 253 | .arg2_type = ARG_PTR_TO_BTF_ID, 254 | .arg2_btf_id = &bpf_file_storage_btf_ids[0], 255 | }; 256 | -------------------------------------------------------------------------------- /kernel/bpf/bpf_ipc_storage.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2020-2021 Harvard University 4 | * Copyright (C) 2020-2021 University of Bristol 5 | * 6 | * Author: Thomas Pasquier 7 | * Author: Bogdan Stelea 8 | * Author: Soo Yee Lim 9 | * Author: Xueyuan "Michael" Han 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License version 2, as 13 | * published by the Free Software Foundation; either version 2 of the License, 14 | * or (at your option) any later version. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | DEFINE_BPF_STORAGE_CACHE(ipc_cache); 34 | 35 | static struct bpf_local_storage __rcu **ipc_storage_ptr(void *owner) 36 | { 37 | struct kern_ipc_perm *ipc = owner; 38 | struct bpf_storage_blob *bsb; 39 | 40 | bsb = bpf_ipc(ipc); 41 | if (!bsb) 42 | return NULL; 43 | return &bsb->storage; 44 | } 45 | 46 | static struct bpf_local_storage_data * 47 | ipc_storage_lookup(struct kern_ipc_perm *ipc, struct bpf_map *map, bool cacheit_lockit) 48 | { 49 | struct bpf_local_storage *ipc_storage; 50 | struct bpf_local_storage_map *smap; 51 | struct bpf_storage_blob *bsb; 52 | 53 | bsb = bpf_ipc(ipc); 54 | if (!bsb) 55 | return NULL; 56 | 57 | ipc_storage = rcu_dereference(bsb->storage); 58 | if (!ipc_storage) 59 | return NULL; 60 | 61 | smap = (struct bpf_local_storage_map *)map; 62 | return bpf_local_storage_lookup(ipc_storage, smap, cacheit_lockit); 63 | } 64 | 65 | void bpf_ipc_storage_free(struct kern_ipc_perm *ipc) 66 | { 67 | struct bpf_local_storage_elem *selem; 68 | struct bpf_local_storage *local_storage; 69 | bool free_ipc_storage = false; 70 | struct bpf_storage_blob *bsb; 71 | struct hlist_node *n; 72 | 73 | bsb = bpf_ipc(ipc); 74 | if (!bsb) 75 | return; 76 | 77 | rcu_read_lock(); 78 | 79 | local_storage = rcu_dereference(bsb->storage); 80 | if (!local_storage) { 81 | rcu_read_unlock(); 82 | return; 83 | } 84 | 85 | /* Neither the bpf_prog nor the bpf-map's syscall 86 | * could be modifying the local_storage->list now. 87 | * Thus, no elem can be added-to or deleted-from the 88 | * local_storage->list by the bpf_prog or by the bpf-map's syscall. 89 | * 90 | * It is racing with bpf_local_storage_map_free() alone 91 | * when unlinking elem from the local_storage->list and 92 | * the map's bucket->list. 93 | */ 94 | raw_spin_lock_bh(&local_storage->lock); 95 | hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { 96 | /* Always unlink from map before unlinking from 97 | * local_storage. 98 | */ 99 | bpf_selem_unlink_map(selem); 100 | free_ipc_storage = bpf_selem_unlink_storage_nolock( 101 | local_storage, selem, false); 102 | } 103 | raw_spin_unlock_bh(&local_storage->lock); 104 | rcu_read_unlock(); 105 | 106 | /* free_ipc_storage should always be true as long as 107 | * local_storage->list was non-empty. 108 | */ 109 | if (free_ipc_storage) 110 | kfree_rcu(local_storage, rcu); 111 | } 112 | 113 | 114 | static void *bpf_ipc_storage_lookup_elem(struct bpf_map *map, void *key) 115 | { 116 | return -ENOTSUPP; 117 | } 118 | 119 | 120 | static int bpf_ipc_storage_update_elem(struct bpf_map *map, void *key, 121 | void *value, u64 map_flags) 122 | { 123 | return -ENOTSUPP; 124 | } 125 | 126 | 127 | static int ipc_storage_delete(struct kern_ipc_perm *ipc, struct bpf_map *map) 128 | { 129 | struct bpf_local_storage_data *sdata; 130 | 131 | sdata = ipc_storage_lookup(ipc, map, false); 132 | if (!sdata) 133 | return -ENOENT; 134 | 135 | bpf_selem_unlink(SELEM(sdata)); 136 | 137 | return 0; 138 | } 139 | 140 | 141 | static int bpf_ipc_storage_delete_elem(struct bpf_map *map, void *key) 142 | { 143 | return -ENOTSUPP; 144 | } 145 | 146 | BPF_CALL_4(bpf_ipc_storage_get, struct bpf_map *, map, struct kern_ipc_perm *, 147 | ipc, void *, value, u64, flags) 148 | { 149 | struct bpf_local_storage_data *sdata; 150 | 151 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) 152 | return (unsigned long)NULL; 153 | 154 | /* explicitly check that the ipc_storage_ptr is not 155 | * NULL as ipc_storage_lookup returns NULL in this case and 156 | * bpf_local_storage_update expects the owner to have a 157 | * valid storage pointer. 158 | */ 159 | if (!ipc || !ipc_storage_ptr(ipc)) 160 | return (unsigned long)NULL; 161 | 162 | sdata = ipc_storage_lookup(ipc, map, true); 163 | if (sdata) 164 | return (unsigned long)sdata->data; 165 | 166 | /* This helper must only be called from places where the lifetime of the ipc 167 | * is guaranteed. Either by being refcounted or by being protected 168 | * by an RCU read-side critical section. 169 | */ 170 | if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { 171 | sdata = bpf_local_storage_update( 172 | ipc, (struct bpf_local_storage_map *)map, value, 173 | BPF_NOEXIST); 174 | return IS_ERR(sdata) ? (unsigned long)NULL : 175 | (unsigned long)sdata->data; 176 | } 177 | 178 | return (unsigned long)NULL; 179 | } 180 | 181 | BPF_CALL_2(bpf_ipc_storage_delete, struct bpf_map *, map, struct kern_ipc_perm *, ipc) 182 | { 183 | if (!ipc) 184 | return -EINVAL; 185 | 186 | /* This helper must only be called from places where the lifetime of the ipc 187 | * is guaranteed. Either by being refcounted or by being protected 188 | * by an RCU read-side critical section. 189 | */ 190 | return ipc_storage_delete(ipc, map); 191 | } 192 | 193 | static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) 194 | { 195 | return -ENOTSUPP; 196 | } 197 | 198 | static struct bpf_map *ipc_storage_map_alloc(union bpf_attr *attr) 199 | { 200 | struct bpf_local_storage_map *smap; 201 | 202 | smap = bpf_local_storage_map_alloc(attr); 203 | if (IS_ERR(smap)) 204 | return ERR_CAST(smap); 205 | 206 | smap->cache_idx = bpf_local_storage_cache_idx_get(&ipc_cache); 207 | return &smap->map; 208 | } 209 | 210 | static void ipc_storage_map_free(struct bpf_map *map) 211 | { 212 | struct bpf_local_storage_map *smap; 213 | 214 | smap = (struct bpf_local_storage_map *)map; 215 | bpf_local_storage_cache_idx_free(&ipc_cache, smap->cache_idx); 216 | bpf_local_storage_map_free(smap); 217 | } 218 | 219 | static int ipc_storage_map_btf_id; 220 | const struct bpf_map_ops ipc_storage_map_ops = { 221 | .map_meta_equal = bpf_map_meta_equal, 222 | .map_alloc_check = bpf_local_storage_map_alloc_check, 223 | .map_alloc = ipc_storage_map_alloc, 224 | .map_free = ipc_storage_map_free, 225 | .map_get_next_key = notsupp_get_next_key, 226 | .map_lookup_elem = bpf_ipc_storage_lookup_elem, 227 | .map_update_elem = bpf_ipc_storage_update_elem, 228 | .map_delete_elem = bpf_ipc_storage_delete_elem, 229 | .map_check_btf = bpf_local_storage_map_check_btf, 230 | .map_btf_name = "bpf_local_storage_map", 231 | .map_btf_id = &ipc_storage_map_btf_id, 232 | .map_owner_storage_ptr = ipc_storage_ptr, 233 | }; 234 | 235 | BTF_ID_LIST_SINGLE(bpf_ipc_storage_btf_ids, struct, kern_ipc_perm) 236 | 237 | const struct bpf_func_proto bpf_ipc_storage_get_proto = { 238 | .func = bpf_ipc_storage_get, 239 | .gpl_only = false, 240 | .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, 241 | .arg1_type = ARG_CONST_MAP_PTR, 242 | .arg2_type = ARG_PTR_TO_BTF_ID, 243 | .arg2_btf_id = &bpf_ipc_storage_btf_ids[0], 244 | .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, 245 | .arg4_type = ARG_ANYTHING, 246 | }; 247 | 248 | const struct bpf_func_proto bpf_ipc_storage_delete_proto = { 249 | .func = bpf_ipc_storage_delete, 250 | .gpl_only = false, 251 | .ret_type = RET_INTEGER, 252 | .arg1_type = ARG_CONST_MAP_PTR, 253 | .arg2_type = ARG_PTR_TO_BTF_ID, 254 | .arg2_btf_id = &bpf_ipc_storage_btf_ids[0], 255 | }; 256 | -------------------------------------------------------------------------------- /kernel/bpf/bpf_lsm.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* 4 | * Copyright (C) 2020 Google LLC. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | /* For every LSM hook that allows attachment of BPF programs, declare a nop 25 | * function where a BPF program can be attached. 26 | */ 27 | #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ 28 | noinline RET bpf_lsm_##NAME(__VA_ARGS__) \ 29 | { \ 30 | return DEFAULT; \ 31 | } 32 | 33 | #include 34 | #undef LSM_HOOK 35 | 36 | #define LSM_HOOK(RET, DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME) 37 | BTF_SET_START(bpf_lsm_hooks) 38 | #include 39 | #undef LSM_HOOK 40 | BTF_SET_END(bpf_lsm_hooks) 41 | 42 | int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, 43 | const struct bpf_prog *prog) 44 | { 45 | if (!prog->gpl_compatible) { 46 | bpf_log(vlog, 47 | "LSM programs must have a GPL compatible license\n"); 48 | return -EINVAL; 49 | } 50 | 51 | if (!btf_id_set_contains(&bpf_lsm_hooks, prog->aux->attach_btf_id)) { 52 | bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n", 53 | prog->aux->attach_btf_id, prog->aux->attach_func_name); 54 | return -EINVAL; 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | /* Mask for all the currently supported BPRM option flags */ 61 | #define BPF_F_BRPM_OPTS_MASK BPF_F_BPRM_SECUREEXEC 62 | 63 | BPF_CALL_2(bpf_bprm_opts_set, struct linux_binprm *, bprm, u64, flags) 64 | { 65 | if (flags & ~BPF_F_BRPM_OPTS_MASK) 66 | return -EINVAL; 67 | 68 | bprm->secureexec = (flags & BPF_F_BPRM_SECUREEXEC); 69 | return 0; 70 | } 71 | 72 | BTF_ID_LIST_SINGLE(bpf_bprm_opts_set_btf_ids, struct, linux_binprm) 73 | 74 | const static struct bpf_func_proto bpf_bprm_opts_set_proto = { 75 | .func = bpf_bprm_opts_set, 76 | .gpl_only = false, 77 | .ret_type = RET_INTEGER, 78 | .arg1_type = ARG_PTR_TO_BTF_ID, 79 | .arg1_btf_id = &bpf_bprm_opts_set_btf_ids[0], 80 | .arg2_type = ARG_ANYTHING, 81 | }; 82 | 83 | BPF_CALL_3(bpf_ima_inode_hash, struct inode *, inode, void *, dst, u32, size) 84 | { 85 | return ima_inode_hash(inode, dst, size); 86 | } 87 | 88 | static bool bpf_ima_inode_hash_allowed(const struct bpf_prog *prog) 89 | { 90 | return bpf_lsm_is_sleepable_hook(prog->aux->attach_btf_id); 91 | } 92 | 93 | BTF_ID_LIST_SINGLE(bpf_ima_inode_hash_btf_ids, struct, inode) 94 | 95 | const static struct bpf_func_proto bpf_ima_inode_hash_proto = { 96 | .func = bpf_ima_inode_hash, 97 | .gpl_only = false, 98 | .ret_type = RET_INTEGER, 99 | .arg1_type = ARG_PTR_TO_BTF_ID, 100 | .arg1_btf_id = &bpf_ima_inode_hash_btf_ids[0], 101 | .arg2_type = ARG_PTR_TO_UNINIT_MEM, 102 | .arg3_type = ARG_CONST_SIZE, 103 | .allowed = bpf_ima_inode_hash_allowed, 104 | }; 105 | 106 | /* systopia contrib start */ 107 | BPF_CALL_1(bpf_inode_from_sock, struct socket *, socket) 108 | { 109 | return (long) SOCK_INODE(socket); 110 | } 111 | 112 | BTF_ID_LIST(bpf_inode_from_sock_btf_ids) 113 | BTF_ID(struct, inode) 114 | BTF_ID(struct, socket) 115 | 116 | const struct bpf_func_proto bpf_inode_from_sock_proto = { 117 | .func = bpf_inode_from_sock, 118 | .gpl_only = false, 119 | .ret_type = RET_PTR_TO_BTF_ID, 120 | .ret_btf_id = &bpf_inode_from_sock_btf_ids[0], 121 | .arg1_type = ARG_PTR_TO_BTF_ID, 122 | .arg1_btf_id = &bpf_inode_from_sock_btf_ids[1], 123 | }; 124 | 125 | BPF_CALL_1(bpf_file_from_fown, struct fown_struct *, fown) 126 | { 127 | return (long) container_of(fown, struct file, f_owner); 128 | } 129 | 130 | BTF_ID_LIST(bpf_file_from_fown_btf_ids) 131 | BTF_ID(struct, file) 132 | BTF_ID(struct, fown_struct) 133 | 134 | const struct bpf_func_proto bpf_file_from_fown_proto = { 135 | .func = bpf_file_from_fown, 136 | .gpl_only = false, 137 | .ret_type = RET_PTR_TO_BTF_ID, 138 | .ret_btf_id = &bpf_file_from_fown_btf_ids[0], 139 | .arg1_type = ARG_PTR_TO_BTF_ID, 140 | .arg1_btf_id = &bpf_file_from_fown_btf_ids[1], 141 | }; 142 | 143 | /* Users need to call dput after use */ 144 | BPF_CALL_1(bpf_dentry_get, struct inode *, inode) 145 | { 146 | return (long) d_find_alias(inode); 147 | } 148 | 149 | BTF_ID_LIST(bpf_dentry_get_btf_ids) 150 | BTF_ID(struct, dentry) 151 | BTF_ID(struct, inode) 152 | 153 | const struct bpf_func_proto bpf_dentry_get_proto = { 154 | .func = bpf_dentry_get, 155 | .gpl_only = false, 156 | .ret_type = RET_PTR_TO_BTF_ID, 157 | .ret_btf_id = &bpf_dentry_get_btf_ids[0], 158 | .arg1_type = ARG_PTR_TO_BTF_ID, 159 | .arg1_btf_id = &bpf_dentry_get_btf_ids[1], 160 | }; 161 | 162 | BPF_CALL_1(bpf_dentry_put, struct dentry *, dentry) 163 | { 164 | dput(dentry); 165 | return 0; 166 | } 167 | 168 | BTF_ID_LIST(bpf_dentry_put_btf_ids) 169 | BTF_ID(struct, dentry) 170 | 171 | const struct bpf_func_proto bpf_dentry_put_proto = { 172 | .func = bpf_dentry_put, 173 | .gpl_only = false, 174 | .ret_type = RET_VOID, 175 | .arg1_type = ARG_PTR_TO_BTF_ID, 176 | .arg1_btf_id = &bpf_dentry_put_btf_ids[0], 177 | }; 178 | 179 | /* systopia contrib end */ 180 | 181 | static const struct bpf_func_proto * 182 | bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 183 | { 184 | switch (func_id) { 185 | case BPF_FUNC_inode_storage_get: 186 | return &bpf_inode_storage_get_proto; 187 | case BPF_FUNC_inode_storage_delete: 188 | return &bpf_inode_storage_delete_proto; 189 | case BPF_FUNC_sk_storage_get: 190 | return &bpf_sk_storage_get_proto; 191 | case BPF_FUNC_sk_storage_delete: 192 | return &bpf_sk_storage_delete_proto; 193 | case BPF_FUNC_spin_lock: 194 | return &bpf_spin_lock_proto; 195 | case BPF_FUNC_spin_unlock: 196 | return &bpf_spin_unlock_proto; 197 | case BPF_FUNC_task_storage_get: 198 | return &bpf_task_storage_get_proto; 199 | case BPF_FUNC_task_storage_delete: 200 | return &bpf_task_storage_delete_proto; 201 | case BPF_FUNC_bprm_opts_set: 202 | return &bpf_bprm_opts_set_proto; 203 | case BPF_FUNC_ima_inode_hash: 204 | return prog->aux->sleepable ? &bpf_ima_inode_hash_proto : NULL; 205 | /* systopia contrib start */ 206 | case BPF_FUNC_inode_from_sock: 207 | return &bpf_inode_from_sock_proto; 208 | case BPF_FUNC_cred_storage_get: 209 | return &bpf_cred_storage_get_proto; 210 | case BPF_FUNC_cred_storage_delete: 211 | return &bpf_cred_storage_delete_proto; 212 | case BPF_FUNC_file_from_fown: 213 | return &bpf_file_from_fown_proto; 214 | case BPF_FUNC_msg_storage_get: 215 | return &bpf_msg_storage_get_proto; 216 | case BPF_FUNC_msg_storage_delete: 217 | return &bpf_msg_storage_delete_proto; 218 | case BPF_FUNC_ipc_storage_get: 219 | return &bpf_ipc_storage_get_proto; 220 | case BPF_FUNC_ipc_storage_delete: 221 | return &bpf_ipc_storage_delete_proto; 222 | case BPF_FUNC_dentry_get: 223 | return &bpf_dentry_get_proto; 224 | case BPF_FUNC_dentry_put: 225 | return &bpf_dentry_put_proto; 226 | case BPF_FUNC_file_storage_get: 227 | return &bpf_file_storage_get_proto; 228 | case BPF_FUNC_file_storage_delete: 229 | return &bpf_file_storage_delete_proto; 230 | /* systopia contrib end */ 231 | default: 232 | return tracing_prog_func_proto(func_id, prog); 233 | } 234 | } 235 | 236 | /* The set of hooks which are called without pagefaults disabled and are allowed 237 | * to "sleep" and thus can be used for sleeable BPF programs. 238 | */ 239 | BTF_SET_START(sleepable_lsm_hooks) 240 | BTF_ID(func, bpf_lsm_bpf) 241 | BTF_ID(func, bpf_lsm_bpf_map) 242 | BTF_ID(func, bpf_lsm_bpf_map_alloc_security) 243 | BTF_ID(func, bpf_lsm_bpf_map_free_security) 244 | BTF_ID(func, bpf_lsm_bpf_prog) 245 | BTF_ID(func, bpf_lsm_bprm_check_security) 246 | BTF_ID(func, bpf_lsm_bprm_committed_creds) 247 | BTF_ID(func, bpf_lsm_bprm_committing_creds) 248 | BTF_ID(func, bpf_lsm_bprm_creds_for_exec) 249 | BTF_ID(func, bpf_lsm_bprm_creds_from_file) 250 | BTF_ID(func, bpf_lsm_capget) 251 | BTF_ID(func, bpf_lsm_capset) 252 | BTF_ID(func, bpf_lsm_cred_prepare) 253 | BTF_ID(func, bpf_lsm_file_ioctl) 254 | BTF_ID(func, bpf_lsm_file_lock) 255 | BTF_ID(func, bpf_lsm_file_open) 256 | BTF_ID(func, bpf_lsm_file_receive) 257 | 258 | #ifdef CONFIG_SECURITY_NETWORK 259 | BTF_ID(func, bpf_lsm_inet_conn_established) 260 | #endif /* CONFIG_SECURITY_NETWORK */ 261 | 262 | BTF_ID(func, bpf_lsm_inode_create) 263 | BTF_ID(func, bpf_lsm_inode_free_security) 264 | BTF_ID(func, bpf_lsm_inode_getattr) 265 | BTF_ID(func, bpf_lsm_inode_getxattr) 266 | BTF_ID(func, bpf_lsm_inode_mknod) 267 | BTF_ID(func, bpf_lsm_inode_need_killpriv) 268 | BTF_ID(func, bpf_lsm_inode_post_setxattr) 269 | BTF_ID(func, bpf_lsm_inode_readlink) 270 | BTF_ID(func, bpf_lsm_inode_rename) 271 | BTF_ID(func, bpf_lsm_inode_rmdir) 272 | BTF_ID(func, bpf_lsm_inode_setattr) 273 | BTF_ID(func, bpf_lsm_inode_setxattr) 274 | BTF_ID(func, bpf_lsm_inode_symlink) 275 | BTF_ID(func, bpf_lsm_inode_unlink) 276 | BTF_ID(func, bpf_lsm_kernel_module_request) 277 | BTF_ID(func, bpf_lsm_kernfs_init_security) 278 | 279 | #ifdef CONFIG_KEYS 280 | BTF_ID(func, bpf_lsm_key_free) 281 | #endif /* CONFIG_KEYS */ 282 | 283 | BTF_ID(func, bpf_lsm_mmap_file) 284 | BTF_ID(func, bpf_lsm_netlink_send) 285 | BTF_ID(func, bpf_lsm_path_notify) 286 | BTF_ID(func, bpf_lsm_release_secctx) 287 | BTF_ID(func, bpf_lsm_sb_alloc_security) 288 | BTF_ID(func, bpf_lsm_sb_eat_lsm_opts) 289 | BTF_ID(func, bpf_lsm_sb_kern_mount) 290 | BTF_ID(func, bpf_lsm_sb_mount) 291 | BTF_ID(func, bpf_lsm_sb_remount) 292 | BTF_ID(func, bpf_lsm_sb_set_mnt_opts) 293 | BTF_ID(func, bpf_lsm_sb_show_options) 294 | BTF_ID(func, bpf_lsm_sb_statfs) 295 | BTF_ID(func, bpf_lsm_sb_umount) 296 | BTF_ID(func, bpf_lsm_settime) 297 | 298 | #ifdef CONFIG_SECURITY_NETWORK 299 | BTF_ID(func, bpf_lsm_socket_accept) 300 | BTF_ID(func, bpf_lsm_socket_bind) 301 | BTF_ID(func, bpf_lsm_socket_connect) 302 | BTF_ID(func, bpf_lsm_socket_create) 303 | BTF_ID(func, bpf_lsm_socket_getpeername) 304 | BTF_ID(func, bpf_lsm_socket_getpeersec_dgram) 305 | BTF_ID(func, bpf_lsm_socket_getsockname) 306 | BTF_ID(func, bpf_lsm_socket_getsockopt) 307 | BTF_ID(func, bpf_lsm_socket_listen) 308 | BTF_ID(func, bpf_lsm_socket_post_create) 309 | BTF_ID(func, bpf_lsm_socket_recvmsg) 310 | BTF_ID(func, bpf_lsm_socket_sendmsg) 311 | BTF_ID(func, bpf_lsm_socket_shutdown) 312 | BTF_ID(func, bpf_lsm_socket_socketpair) 313 | #endif /* CONFIG_SECURITY_NETWORK */ 314 | 315 | BTF_ID(func, bpf_lsm_syslog) 316 | BTF_ID(func, bpf_lsm_task_alloc) 317 | BTF_ID(func, bpf_lsm_task_getsecid) 318 | BTF_ID(func, bpf_lsm_task_prctl) 319 | BTF_ID(func, bpf_lsm_task_setscheduler) 320 | BTF_ID(func, bpf_lsm_task_to_inode) 321 | BTF_SET_END(sleepable_lsm_hooks) 322 | 323 | bool bpf_lsm_is_sleepable_hook(u32 btf_id) 324 | { 325 | return btf_id_set_contains(&sleepable_lsm_hooks, btf_id); 326 | } 327 | 328 | const struct bpf_prog_ops lsm_prog_ops = { 329 | }; 330 | 331 | const struct bpf_verifier_ops lsm_verifier_ops = { 332 | .get_func_proto = bpf_lsm_func_proto, 333 | .is_valid_access = btf_ctx_access, 334 | }; 335 | -------------------------------------------------------------------------------- /kernel/bpf/bpf_msg_storage.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * Copyright (C) 2020-2021 Harvard University 4 | * Copyright (C) 2020-2021 University of Bristol 5 | * 6 | * Author: Thomas Pasquier 7 | * Author: Bogdan Stelea 8 | * Author: Soo Yee Lim 9 | * Author: Xueyuan "Michael" Han 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License version 2, as 13 | * published by the Free Software Foundation; either version 2 of the License, 14 | * or (at your option) any later version. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | DEFINE_BPF_STORAGE_CACHE(msg_cache); 34 | 35 | static struct bpf_local_storage __rcu **msg_storage_ptr(void *owner) 36 | { 37 | struct msg_msg *msg = owner; 38 | struct bpf_storage_blob *bsb; 39 | 40 | bsb = bpf_msg(msg); 41 | if (!bsb) 42 | return NULL; 43 | return &bsb->storage; 44 | } 45 | 46 | static struct bpf_local_storage_data * 47 | msg_storage_lookup(struct msg_msg *msg, struct bpf_map *map, bool cacheit_lockit) 48 | { 49 | struct bpf_local_storage *msg_storage; 50 | struct bpf_local_storage_map *smap; 51 | struct bpf_storage_blob *bsb; 52 | 53 | bsb = bpf_msg(msg); 54 | if (!bsb) 55 | return NULL; 56 | 57 | msg_storage = rcu_dereference(bsb->storage); 58 | if (!msg_storage) 59 | return NULL; 60 | 61 | smap = (struct bpf_local_storage_map *)map; 62 | return bpf_local_storage_lookup(msg_storage, smap, cacheit_lockit); 63 | } 64 | 65 | void bpf_msg_storage_free(struct msg_msg *msg) 66 | { 67 | struct bpf_local_storage_elem *selem; 68 | struct bpf_local_storage *local_storage; 69 | bool free_msg_storage = false; 70 | struct bpf_storage_blob *bsb; 71 | struct hlist_node *n; 72 | 73 | bsb = bpf_msg(msg); 74 | if (!bsb) 75 | return; 76 | 77 | rcu_read_lock(); 78 | 79 | local_storage = rcu_dereference(bsb->storage); 80 | if (!local_storage) { 81 | rcu_read_unlock(); 82 | return; 83 | } 84 | 85 | /* Neither the bpf_prog nor the bpf-map's syscall 86 | * could be modifying the local_storage->list now. 87 | * Thus, no elem can be added-to or deleted-from the 88 | * local_storage->list by the bpf_prog or by the bpf-map's syscall. 89 | * 90 | * It is racing with bpf_local_storage_map_free() alone 91 | * when unlinking elem from the local_storage->list and 92 | * the map's bucket->list. 93 | */ 94 | raw_spin_lock_bh(&local_storage->lock); 95 | hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { 96 | /* Always unlink from map before unlinking from 97 | * local_storage. 98 | */ 99 | bpf_selem_unlink_map(selem); 100 | free_msg_storage = bpf_selem_unlink_storage_nolock( 101 | local_storage, selem, false); 102 | } 103 | raw_spin_unlock_bh(&local_storage->lock); 104 | rcu_read_unlock(); 105 | 106 | /* free_msg_storage should always be true as long as 107 | * local_storage->list was non-empty. 108 | */ 109 | if (free_msg_storage) 110 | kfree_rcu(local_storage, rcu); 111 | } 112 | 113 | 114 | static void *bpf_msqid_msg_storage_lookup_elem(struct bpf_map *map, void *key) 115 | { 116 | return -ENOTSUPP; 117 | } 118 | 119 | 120 | static int bpf_msqid_msg_storage_update_elem(struct bpf_map *map, void *key, 121 | void *value, u64 map_flags) 122 | { 123 | return -ENOTSUPP; 124 | } 125 | 126 | 127 | static int msg_storage_delete(struct msg_msg *msg, struct bpf_map *map) 128 | { 129 | struct bpf_local_storage_data *sdata; 130 | 131 | sdata = msg_storage_lookup(msg, map, false); 132 | if (!sdata) 133 | return -ENOENT; 134 | 135 | bpf_selem_unlink(SELEM(sdata)); 136 | 137 | return 0; 138 | } 139 | 140 | 141 | static int bpf_msqid_msg_storage_delete_elem(struct bpf_map *map, void *key) 142 | { 143 | return -ENOTSUPP; 144 | } 145 | 146 | BPF_CALL_4(bpf_msg_storage_get, struct bpf_map *, map, struct msg_msg *, 147 | msg, void *, value, u64, flags) 148 | { 149 | struct bpf_local_storage_data *sdata; 150 | 151 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) 152 | return (unsigned long)NULL; 153 | 154 | /* explicitly check that the msg_storage_ptr is not 155 | * NULL as msg_storage_lookup returns NULL in this case and 156 | * bpf_local_storage_update expects the owner to have a 157 | * valid storage pointer. 158 | */ 159 | if (!msg || !msg_storage_ptr(msg)) 160 | return (unsigned long)NULL; 161 | 162 | sdata = msg_storage_lookup(msg, map, true); 163 | if (sdata) 164 | return (unsigned long)sdata->data; 165 | 166 | /* This helper must only be called from places where the lifetime of the msg 167 | * is guaranteed. Either by being refcounted or by being protected 168 | * by an RCU read-side critical section. 169 | */ 170 | if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { 171 | sdata = bpf_local_storage_update( 172 | msg, (struct bpf_local_storage_map *)map, value, 173 | BPF_NOEXIST); 174 | return IS_ERR(sdata) ? (unsigned long)NULL : 175 | (unsigned long)sdata->data; 176 | } 177 | 178 | return (unsigned long)NULL; 179 | } 180 | 181 | BPF_CALL_2(bpf_msg_storage_delete, struct bpf_map *, map, struct msg_msg *, msg) 182 | { 183 | if (!msg) 184 | return -EINVAL; 185 | 186 | /* This helper must only be called from places where the lifetime of the msg 187 | * is guaranteed. Either by being refcounted or by being protected 188 | * by an RCU read-side critical section. 189 | */ 190 | return msg_storage_delete(msg, map); 191 | } 192 | 193 | static int notsupp_get_next_key(struct bpf_map *map, void *key, void *next_key) 194 | { 195 | return -ENOTSUPP; 196 | } 197 | 198 | static struct bpf_map *msg_storage_map_alloc(union bpf_attr *attr) 199 | { 200 | struct bpf_local_storage_map *smap; 201 | 202 | smap = bpf_local_storage_map_alloc(attr); 203 | if (IS_ERR(smap)) 204 | return ERR_CAST(smap); 205 | 206 | smap->cache_idx = bpf_local_storage_cache_idx_get(&msg_cache); 207 | return &smap->map; 208 | } 209 | 210 | static void msg_storage_map_free(struct bpf_map *map) 211 | { 212 | struct bpf_local_storage_map *smap; 213 | 214 | smap = (struct bpf_local_storage_map *)map; 215 | bpf_local_storage_cache_idx_free(&msg_cache, smap->cache_idx); 216 | bpf_local_storage_map_free(smap); 217 | } 218 | 219 | static int msg_storage_map_btf_id; 220 | const struct bpf_map_ops msg_storage_map_ops = { 221 | .map_meta_equal = bpf_map_meta_equal, 222 | .map_alloc_check = bpf_local_storage_map_alloc_check, 223 | .map_alloc = msg_storage_map_alloc, 224 | .map_free = msg_storage_map_free, 225 | .map_get_next_key = notsupp_get_next_key, 226 | .map_lookup_elem = bpf_msqid_msg_storage_lookup_elem, 227 | .map_update_elem = bpf_msqid_msg_storage_update_elem, 228 | .map_delete_elem = bpf_msqid_msg_storage_delete_elem, 229 | .map_check_btf = bpf_local_storage_map_check_btf, 230 | .map_btf_name = "bpf_local_storage_map", 231 | .map_btf_id = &msg_storage_map_btf_id, 232 | .map_owner_storage_ptr = msg_storage_ptr, 233 | }; 234 | 235 | BTF_ID_LIST_SINGLE(bpf_msg_storage_btf_ids, struct, msg_msg) 236 | 237 | const struct bpf_func_proto bpf_msg_storage_get_proto = { 238 | .func = bpf_msg_storage_get, 239 | .gpl_only = false, 240 | .ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL, 241 | .arg1_type = ARG_CONST_MAP_PTR, 242 | .arg2_type = ARG_PTR_TO_BTF_ID, 243 | .arg2_btf_id = &bpf_msg_storage_btf_ids[0], 244 | .arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL, 245 | .arg4_type = ARG_ANYTHING, 246 | }; 247 | 248 | const struct bpf_func_proto bpf_msg_storage_delete_proto = { 249 | .func = bpf_msg_storage_delete, 250 | .gpl_only = false, 251 | .ret_type = RET_INTEGER, 252 | .arg1_type = ARG_CONST_MAP_PTR, 253 | .arg2_type = ARG_PTR_TO_BTF_ID, 254 | .arg2_btf_id = &bpf_msg_storage_btf_ids[0], 255 | }; 256 | -------------------------------------------------------------------------------- /scripts/bpf_helpers_doc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # Copyright (C) 2018-2019 Netronome Systems, Inc. 5 | 6 | # In case user attempts to run with Python 2. 7 | from __future__ import print_function 8 | 9 | import argparse 10 | import re 11 | import sys, os 12 | 13 | class NoHelperFound(BaseException): 14 | pass 15 | 16 | class ParsingError(BaseException): 17 | def __init__(self, line='', reader=None): 18 | if reader: 19 | BaseException.__init__(self, 20 | 'Error at file offset %d, parsing line: %s' % 21 | (reader.tell(), line)) 22 | else: 23 | BaseException.__init__(self, 'Error parsing line: %s' % line) 24 | 25 | class Helper(object): 26 | """ 27 | An object representing the description of an eBPF helper function. 28 | @proto: function prototype of the helper function 29 | @desc: textual description of the helper function 30 | @ret: description of the return value of the helper function 31 | """ 32 | def __init__(self, proto='', desc='', ret=''): 33 | self.proto = proto 34 | self.desc = desc 35 | self.ret = ret 36 | 37 | def proto_break_down(self): 38 | """ 39 | Break down helper function protocol into smaller chunks: return type, 40 | name, distincts arguments. 41 | """ 42 | arg_re = re.compile('((\w+ )*?(\w+|...))( (\**)(\w+))?$') 43 | res = {} 44 | proto_re = re.compile('(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$') 45 | 46 | capture = proto_re.match(self.proto) 47 | res['ret_type'] = capture.group(1) 48 | res['ret_star'] = capture.group(2) 49 | res['name'] = capture.group(3) 50 | res['args'] = [] 51 | 52 | args = capture.group(4).split(', ') 53 | for a in args: 54 | capture = arg_re.match(a) 55 | res['args'].append({ 56 | 'type' : capture.group(1), 57 | 'star' : capture.group(5), 58 | 'name' : capture.group(6) 59 | }) 60 | 61 | return res 62 | 63 | class HeaderParser(object): 64 | """ 65 | An object used to parse a file in order to extract the documentation of a 66 | list of eBPF helper functions. All the helpers that can be retrieved are 67 | stored as Helper object, in the self.helpers() array. 68 | @filename: name of file to parse, usually include/uapi/linux/bpf.h in the 69 | kernel tree 70 | """ 71 | def __init__(self, filename): 72 | self.reader = open(filename, 'r') 73 | self.line = '' 74 | self.helpers = [] 75 | 76 | def parse_helper(self): 77 | proto = self.parse_proto() 78 | desc = self.parse_desc() 79 | ret = self.parse_ret() 80 | return Helper(proto=proto, desc=desc, ret=ret) 81 | 82 | def parse_proto(self): 83 | # Argument can be of shape: 84 | # - "void" 85 | # - "type name" 86 | # - "type *name" 87 | # - Same as above, with "const" and/or "struct" in front of type 88 | # - "..." (undefined number of arguments, for bpf_trace_printk()) 89 | # There is at least one term ("void"), and at most five arguments. 90 | p = re.compile(' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$') 91 | capture = p.match(self.line) 92 | if not capture: 93 | raise NoHelperFound 94 | self.line = self.reader.readline() 95 | return capture.group(1) 96 | 97 | def parse_desc(self): 98 | p = re.compile(' \* ?(?:\t| {5,8})Description$') 99 | capture = p.match(self.line) 100 | if not capture: 101 | # Helper can have empty description and we might be parsing another 102 | # attribute: return but do not consume. 103 | return '' 104 | # Description can be several lines, some of them possibly empty, and it 105 | # stops when another subsection title is met. 106 | desc = '' 107 | while True: 108 | self.line = self.reader.readline() 109 | if self.line == ' *\n': 110 | desc += '\n' 111 | else: 112 | p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') 113 | capture = p.match(self.line) 114 | if capture: 115 | desc += capture.group(1) + '\n' 116 | else: 117 | break 118 | return desc 119 | 120 | def parse_ret(self): 121 | p = re.compile(' \* ?(?:\t| {5,8})Return$') 122 | capture = p.match(self.line) 123 | if not capture: 124 | # Helper can have empty retval and we might be parsing another 125 | # attribute: return but do not consume. 126 | return '' 127 | # Return value description can be several lines, some of them possibly 128 | # empty, and it stops when another subsection title is met. 129 | ret = '' 130 | while True: 131 | self.line = self.reader.readline() 132 | if self.line == ' *\n': 133 | ret += '\n' 134 | else: 135 | p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') 136 | capture = p.match(self.line) 137 | if capture: 138 | ret += capture.group(1) + '\n' 139 | else: 140 | break 141 | return ret 142 | 143 | def run(self): 144 | # Advance to start of helper function descriptions. 145 | offset = self.reader.read().find('* Start of BPF helper function descriptions:') 146 | if offset == -1: 147 | raise Exception('Could not find start of eBPF helper descriptions list') 148 | self.reader.seek(offset) 149 | self.reader.readline() 150 | self.reader.readline() 151 | self.line = self.reader.readline() 152 | 153 | while True: 154 | try: 155 | helper = self.parse_helper() 156 | self.helpers.append(helper) 157 | except NoHelperFound: 158 | break 159 | 160 | self.reader.close() 161 | 162 | ############################################################################### 163 | 164 | class Printer(object): 165 | """ 166 | A generic class for printers. Printers should be created with an array of 167 | Helper objects, and implement a way to print them in the desired fashion. 168 | @helpers: array of Helper objects to print to standard output 169 | """ 170 | def __init__(self, helpers): 171 | self.helpers = helpers 172 | 173 | def print_header(self): 174 | pass 175 | 176 | def print_footer(self): 177 | pass 178 | 179 | def print_one(self, helper): 180 | pass 181 | 182 | def print_all(self): 183 | self.print_header() 184 | for helper in self.helpers: 185 | self.print_one(helper) 186 | self.print_footer() 187 | 188 | class PrinterRST(Printer): 189 | """ 190 | A printer for dumping collected information about helpers as a ReStructured 191 | Text page compatible with the rst2man program, which can be used to 192 | generate a manual page for the helpers. 193 | @helpers: array of Helper objects to print to standard output 194 | """ 195 | def print_header(self): 196 | header = '''\ 197 | .. Copyright (C) All BPF authors and contributors from 2014 to present. 198 | .. See git log include/uapi/linux/bpf.h in kernel tree for details. 199 | .. 200 | .. %%%LICENSE_START(VERBATIM) 201 | .. Permission is granted to make and distribute verbatim copies of this 202 | .. manual provided the copyright notice and this permission notice are 203 | .. preserved on all copies. 204 | .. 205 | .. Permission is granted to copy and distribute modified versions of this 206 | .. manual under the conditions for verbatim copying, provided that the 207 | .. entire resulting derived work is distributed under the terms of a 208 | .. permission notice identical to this one. 209 | .. 210 | .. Since the Linux kernel and libraries are constantly changing, this 211 | .. manual page may be incorrect or out-of-date. The author(s) assume no 212 | .. responsibility for errors or omissions, or for damages resulting from 213 | .. the use of the information contained herein. The author(s) may not 214 | .. have taken the same level of care in the production of this manual, 215 | .. which is licensed free of charge, as they might when working 216 | .. professionally. 217 | .. 218 | .. Formatted or processed versions of this manual, if unaccompanied by 219 | .. the source, must acknowledge the copyright and authors of this work. 220 | .. %%%LICENSE_END 221 | .. 222 | .. Please do not edit this file. It was generated from the documentation 223 | .. located in file include/uapi/linux/bpf.h of the Linux kernel sources 224 | .. (helpers description), and from scripts/bpf_helpers_doc.py in the same 225 | .. repository (header and footer). 226 | 227 | =========== 228 | BPF-HELPERS 229 | =========== 230 | ------------------------------------------------------------------------------- 231 | list of eBPF helper functions 232 | ------------------------------------------------------------------------------- 233 | 234 | :Manual section: 7 235 | 236 | DESCRIPTION 237 | =========== 238 | 239 | The extended Berkeley Packet Filter (eBPF) subsystem consists in programs 240 | written in a pseudo-assembly language, then attached to one of the several 241 | kernel hooks and run in reaction of specific events. This framework differs 242 | from the older, "classic" BPF (or "cBPF") in several aspects, one of them being 243 | the ability to call special functions (or "helpers") from within a program. 244 | These functions are restricted to a white-list of helpers defined in the 245 | kernel. 246 | 247 | These helpers are used by eBPF programs to interact with the system, or with 248 | the context in which they work. For instance, they can be used to print 249 | debugging messages, to get the time since the system was booted, to interact 250 | with eBPF maps, or to manipulate network packets. Since there are several eBPF 251 | program types, and that they do not run in the same context, each program type 252 | can only call a subset of those helpers. 253 | 254 | Due to eBPF conventions, a helper can not have more than five arguments. 255 | 256 | Internally, eBPF programs call directly into the compiled helper functions 257 | without requiring any foreign-function interface. As a result, calling helpers 258 | introduces no overhead, thus offering excellent performance. 259 | 260 | This document is an attempt to list and document the helpers available to eBPF 261 | developers. They are sorted by chronological order (the oldest helpers in the 262 | kernel at the top). 263 | 264 | HELPERS 265 | ======= 266 | ''' 267 | print(header) 268 | 269 | def print_footer(self): 270 | footer = ''' 271 | EXAMPLES 272 | ======== 273 | 274 | Example usage for most of the eBPF helpers listed in this manual page are 275 | available within the Linux kernel sources, at the following locations: 276 | 277 | * *samples/bpf/* 278 | * *tools/testing/selftests/bpf/* 279 | 280 | LICENSE 281 | ======= 282 | 283 | eBPF programs can have an associated license, passed along with the bytecode 284 | instructions to the kernel when the programs are loaded. The format for that 285 | string is identical to the one in use for kernel modules (Dual licenses, such 286 | as "Dual BSD/GPL", may be used). Some helper functions are only accessible to 287 | programs that are compatible with the GNU Privacy License (GPL). 288 | 289 | In order to use such helpers, the eBPF program must be loaded with the correct 290 | license string passed (via **attr**) to the **bpf**\ () system call, and this 291 | generally translates into the C source code of the program containing a line 292 | similar to the following: 293 | 294 | :: 295 | 296 | char ____license[] __attribute__((section("license"), used)) = "GPL"; 297 | 298 | IMPLEMENTATION 299 | ============== 300 | 301 | This manual page is an effort to document the existing eBPF helper functions. 302 | But as of this writing, the BPF sub-system is under heavy development. New eBPF 303 | program or map types are added, along with new helper functions. Some helpers 304 | are occasionally made available for additional program types. So in spite of 305 | the efforts of the community, this page might not be up-to-date. If you want to 306 | check by yourself what helper functions exist in your kernel, or what types of 307 | programs they can support, here are some files among the kernel tree that you 308 | may be interested in: 309 | 310 | * *include/uapi/linux/bpf.h* is the main BPF header. It contains the full list 311 | of all helper functions, as well as many other BPF definitions including most 312 | of the flags, structs or constants used by the helpers. 313 | * *net/core/filter.c* contains the definition of most network-related helper 314 | functions, and the list of program types from which they can be used. 315 | * *kernel/trace/bpf_trace.c* is the equivalent for most tracing program-related 316 | helpers. 317 | * *kernel/bpf/verifier.c* contains the functions used to check that valid types 318 | of eBPF maps are used with a given helper function. 319 | * *kernel/bpf/* directory contains other files in which additional helpers are 320 | defined (for cgroups, sockmaps, etc.). 321 | * The bpftool utility can be used to probe the availability of helper functions 322 | on the system (as well as supported program and map types, and a number of 323 | other parameters). To do so, run **bpftool feature probe** (see 324 | **bpftool-feature**\ (8) for details). Add the **unprivileged** keyword to 325 | list features available to unprivileged users. 326 | 327 | Compatibility between helper functions and program types can generally be found 328 | in the files where helper functions are defined. Look for the **struct 329 | bpf_func_proto** objects and for functions returning them: these functions 330 | contain a list of helpers that a given program type can call. Note that the 331 | **default:** label of the **switch ... case** used to filter helpers can call 332 | other functions, themselves allowing access to additional helpers. The 333 | requirement for GPL license is also in those **struct bpf_func_proto**. 334 | 335 | Compatibility between helper functions and map types can be found in the 336 | **check_map_func_compatibility**\ () function in file *kernel/bpf/verifier.c*. 337 | 338 | Helper functions that invalidate the checks on **data** and **data_end** 339 | pointers for network processing are listed in function 340 | **bpf_helper_changes_pkt_data**\ () in file *net/core/filter.c*. 341 | 342 | SEE ALSO 343 | ======== 344 | 345 | **bpf**\ (2), 346 | **bpftool**\ (8), 347 | **cgroups**\ (7), 348 | **ip**\ (8), 349 | **perf_event_open**\ (2), 350 | **sendmsg**\ (2), 351 | **socket**\ (7), 352 | **tc-bpf**\ (8)''' 353 | print(footer) 354 | 355 | def print_proto(self, helper): 356 | """ 357 | Format function protocol with bold and italics markers. This makes RST 358 | file less readable, but gives nice results in the manual page. 359 | """ 360 | proto = helper.proto_break_down() 361 | 362 | print('**%s %s%s(' % (proto['ret_type'], 363 | proto['ret_star'].replace('*', '\\*'), 364 | proto['name']), 365 | end='') 366 | 367 | comma = '' 368 | for a in proto['args']: 369 | one_arg = '{}{}'.format(comma, a['type']) 370 | if a['name']: 371 | if a['star']: 372 | one_arg += ' {}**\ '.format(a['star'].replace('*', '\\*')) 373 | else: 374 | one_arg += '** ' 375 | one_arg += '*{}*\\ **'.format(a['name']) 376 | comma = ', ' 377 | print(one_arg, end='') 378 | 379 | print(')**') 380 | 381 | def print_one(self, helper): 382 | self.print_proto(helper) 383 | 384 | if (helper.desc): 385 | print('\tDescription') 386 | # Do not strip all newline characters: formatted code at the end of 387 | # a section must be followed by a blank line. 388 | for line in re.sub('\n$', '', helper.desc, count=1).split('\n'): 389 | print('{}{}'.format('\t\t' if line else '', line)) 390 | 391 | if (helper.ret): 392 | print('\tReturn') 393 | for line in helper.ret.rstrip().split('\n'): 394 | print('{}{}'.format('\t\t' if line else '', line)) 395 | 396 | print('') 397 | 398 | class PrinterHelpers(Printer): 399 | """ 400 | A printer for dumping collected information about helpers as C header to 401 | be included from BPF program. 402 | @helpers: array of Helper objects to print to standard output 403 | """ 404 | 405 | type_fwds = [ 406 | 'struct bpf_fib_lookup', 407 | 'struct bpf_sk_lookup', 408 | 'struct bpf_perf_event_data', 409 | 'struct bpf_perf_event_value', 410 | 'struct bpf_pidns_info', 411 | 'struct bpf_redir_neigh', 412 | 'struct bpf_sock', 413 | 'struct bpf_sock_addr', 414 | 'struct bpf_sock_ops', 415 | 'struct bpf_sock_tuple', 416 | 'struct bpf_spin_lock', 417 | 'struct bpf_sysctl', 418 | 'struct bpf_tcp_sock', 419 | 'struct bpf_tunnel_key', 420 | 'struct bpf_xfrm_state', 421 | 'struct linux_binprm', 422 | 'struct pt_regs', 423 | 'struct sk_reuseport_md', 424 | 'struct sockaddr', 425 | 'struct tcphdr', 426 | 'struct seq_file', 427 | 'struct tcp6_sock', 428 | 'struct tcp_sock', 429 | 'struct tcp_timewait_sock', 430 | 'struct tcp_request_sock', 431 | 'struct udp6_sock', 432 | 'struct task_struct', 433 | 434 | 'struct __sk_buff', 435 | 'struct sk_msg_md', 436 | 'struct xdp_md', 437 | 'struct path', 438 | 'struct btf_ptr', 439 | 'struct inode', 440 | 'struct socket', 441 | 'struct file', 442 | # systopia contrib start # 443 | 'struct cred', 444 | 'struct fown_struct', 445 | 'struct msg_msg', 446 | 'struct kern_ipc_perm', 447 | 'struct dentry', 448 | # systopia contrib end # 449 | ] 450 | known_types = { 451 | '...', 452 | 'void', 453 | 'const void', 454 | 'char', 455 | 'const char', 456 | 'int', 457 | 'long', 458 | 'unsigned long', 459 | 460 | '__be16', 461 | '__be32', 462 | '__wsum', 463 | 464 | 'struct bpf_fib_lookup', 465 | 'struct bpf_perf_event_data', 466 | 'struct bpf_perf_event_value', 467 | 'struct bpf_pidns_info', 468 | 'struct bpf_redir_neigh', 469 | 'struct bpf_sk_lookup', 470 | 'struct bpf_sock', 471 | 'struct bpf_sock_addr', 472 | 'struct bpf_sock_ops', 473 | 'struct bpf_sock_tuple', 474 | 'struct bpf_spin_lock', 475 | 'struct bpf_sysctl', 476 | 'struct bpf_tcp_sock', 477 | 'struct bpf_tunnel_key', 478 | 'struct bpf_xfrm_state', 479 | 'struct linux_binprm', 480 | 'struct pt_regs', 481 | 'struct sk_reuseport_md', 482 | 'struct sockaddr', 483 | 'struct tcphdr', 484 | 'struct seq_file', 485 | 'struct tcp6_sock', 486 | 'struct tcp_sock', 487 | 'struct tcp_timewait_sock', 488 | 'struct tcp_request_sock', 489 | 'struct udp6_sock', 490 | 'struct task_struct', 491 | 'struct path', 492 | 'struct btf_ptr', 493 | 'struct inode', 494 | 'struct socket', 495 | 'struct file', 496 | # systopia contrib start # 497 | 'struct cred', 498 | 'struct fown_struct', 499 | 'struct msg_msg', 500 | 'struct kern_ipc_perm', 501 | 'struct dentry', 502 | # systopia contrib end # 503 | } 504 | mapped_types = { 505 | 'u8': '__u8', 506 | 'u16': '__u16', 507 | 'u32': '__u32', 508 | 'u64': '__u64', 509 | 's8': '__s8', 510 | 's16': '__s16', 511 | 's32': '__s32', 512 | 's64': '__s64', 513 | 'size_t': 'unsigned long', 514 | 'struct bpf_map': 'void', 515 | 'struct sk_buff': 'struct __sk_buff', 516 | 'const struct sk_buff': 'const struct __sk_buff', 517 | 'struct sk_msg_buff': 'struct sk_msg_md', 518 | 'struct xdp_buff': 'struct xdp_md', 519 | } 520 | # Helpers overloaded for different context types. 521 | overloaded_helpers = [ 522 | 'bpf_get_socket_cookie', 523 | 'bpf_sk_assign', 524 | ] 525 | 526 | def print_header(self): 527 | header = '''\ 528 | /* This is auto-generated file. See bpf_helpers_doc.py for details. */ 529 | 530 | /* Forward declarations of BPF structs */''' 531 | 532 | print(header) 533 | for fwd in self.type_fwds: 534 | print('%s;' % fwd) 535 | print('') 536 | 537 | def print_footer(self): 538 | footer = '' 539 | print(footer) 540 | 541 | def map_type(self, t): 542 | if t in self.known_types: 543 | return t 544 | if t in self.mapped_types: 545 | return self.mapped_types[t] 546 | print("Unrecognized type '%s', please add it to known types!" % t, 547 | file=sys.stderr) 548 | sys.exit(1) 549 | 550 | seen_helpers = set() 551 | 552 | def print_one(self, helper): 553 | proto = helper.proto_break_down() 554 | 555 | if proto['name'] in self.seen_helpers: 556 | return 557 | self.seen_helpers.add(proto['name']) 558 | 559 | print('/*') 560 | print(" * %s" % proto['name']) 561 | print(" *") 562 | if (helper.desc): 563 | # Do not strip all newline characters: formatted code at the end of 564 | # a section must be followed by a blank line. 565 | for line in re.sub('\n$', '', helper.desc, count=1).split('\n'): 566 | print(' *{}{}'.format(' \t' if line else '', line)) 567 | 568 | if (helper.ret): 569 | print(' *') 570 | print(' * Returns') 571 | for line in helper.ret.rstrip().split('\n'): 572 | print(' *{}{}'.format(' \t' if line else '', line)) 573 | 574 | print(' */') 575 | print('static %s %s(*%s)(' % (self.map_type(proto['ret_type']), 576 | proto['ret_star'], proto['name']), end='') 577 | comma = '' 578 | for i, a in enumerate(proto['args']): 579 | t = a['type'] 580 | n = a['name'] 581 | if proto['name'] in self.overloaded_helpers and i == 0: 582 | t = 'void' 583 | n = 'ctx' 584 | one_arg = '{}{}'.format(comma, self.map_type(t)) 585 | if n: 586 | if a['star']: 587 | one_arg += ' {}'.format(a['star']) 588 | else: 589 | one_arg += ' ' 590 | one_arg += '{}'.format(n) 591 | comma = ', ' 592 | print(one_arg, end='') 593 | 594 | print(') = (void *) %d;' % len(self.seen_helpers)) 595 | print('') 596 | 597 | ############################################################################### 598 | 599 | # If script is launched from scripts/ from kernel tree and can access 600 | # ../include/uapi/linux/bpf.h, use it as a default name for the file to parse, 601 | # otherwise the --filename argument will be required from the command line. 602 | script = os.path.abspath(sys.argv[0]) 603 | linuxRoot = os.path.dirname(os.path.dirname(script)) 604 | bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') 605 | 606 | argParser = argparse.ArgumentParser(description=""" 607 | Parse eBPF header file and generate documentation for eBPF helper functions. 608 | The RST-formatted output produced can be turned into a manual page with the 609 | rst2man utility. 610 | """) 611 | argParser.add_argument('--header', action='store_true', 612 | help='generate C header file') 613 | if (os.path.isfile(bpfh)): 614 | argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h', 615 | default=bpfh) 616 | else: 617 | argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h') 618 | args = argParser.parse_args() 619 | 620 | # Parse file. 621 | headerParser = HeaderParser(args.filename) 622 | headerParser.run() 623 | 624 | # Print formatted output to standard output. 625 | if args.header: 626 | printer = PrinterHelpers(headerParser.helpers) 627 | else: 628 | printer = PrinterRST(headerParser.helpers) 629 | printer.print_all() 630 | -------------------------------------------------------------------------------- /security/bpf/hooks.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* 4 | * Copyright (C) 2020 Google LLC. 5 | */ 6 | #include 7 | #include 8 | 9 | static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = { 10 | #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ 11 | LSM_HOOK_INIT(NAME, bpf_lsm_##NAME), 12 | #include 13 | #undef LSM_HOOK 14 | LSM_HOOK_INIT(inode_free_security, bpf_inode_storage_free), 15 | LSM_HOOK_INIT(task_free, bpf_task_storage_free), 16 | /* systopia contrib start */ 17 | LSM_HOOK_INIT(cred_free, bpf_cred_storage_free), 18 | LSM_HOOK_INIT(msg_msg_free_security, bpf_msg_storage_free), 19 | LSM_HOOK_INIT(sem_free_security, bpf_ipc_storage_free), 20 | LSM_HOOK_INIT(file_free_security, bpf_file_storage_free), 21 | /* systopia contrib end */ 22 | }; 23 | 24 | static int __init bpf_lsm_init(void) 25 | { 26 | security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks), "bpf"); 27 | pr_info("LSM support for eBPF active\n"); 28 | return 0; 29 | } 30 | 31 | struct lsm_blob_sizes bpf_lsm_blob_sizes __lsm_ro_after_init = { 32 | .lbs_inode = sizeof(struct bpf_storage_blob), 33 | .lbs_task = sizeof(struct bpf_storage_blob), 34 | /* systopia contrib start */ 35 | .lbs_cred = sizeof(struct bpf_storage_blob), 36 | .lbs_msg_msg = sizeof(struct bpf_storage_blob), 37 | .lbs_ipc = sizeof(struct bpf_storage_blob), 38 | .lbs_file = sizeof(struct bpf_storage_blob), 39 | /* systopia contrib end */ 40 | }; 41 | 42 | DEFINE_LSM(bpf) = { 43 | .name = "bpf", 44 | .init = bpf_lsm_init, 45 | .blobs = &bpf_lsm_blob_sizes 46 | }; 47 | -------------------------------------------------------------------------------- /tools/bpf/bpftool/Documentation/bpftool-map.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | bpftool-map 3 | ================ 4 | ------------------------------------------------------------------------------- 5 | tool for inspection and simple manipulation of eBPF maps 6 | ------------------------------------------------------------------------------- 7 | 8 | :Manual section: 8 9 | 10 | SYNOPSIS 11 | ======== 12 | 13 | **bpftool** [*OPTIONS*] **map** *COMMAND* 14 | 15 | *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } } 16 | 17 | *COMMANDS* := 18 | { **show** | **list** | **create** | **dump** | **update** | **lookup** | **getnext** 19 | | **delete** | **pin** | **help** } 20 | 21 | MAP COMMANDS 22 | ============= 23 | 24 | | **bpftool** **map** { **show** | **list** } [*MAP*] 25 | | **bpftool** **map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* \ 26 | | **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] \ 27 | | [**dev** *NAME*] 28 | | **bpftool** **map dump** *MAP* 29 | | **bpftool** **map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*] 30 | | **bpftool** **map lookup** *MAP* [**key** *DATA*] 31 | | **bpftool** **map getnext** *MAP* [**key** *DATA*] 32 | | **bpftool** **map delete** *MAP* **key** *DATA* 33 | | **bpftool** **map pin** *MAP* *FILE* 34 | | **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*] 35 | | **bpftool** **map peek** *MAP* 36 | | **bpftool** **map push** *MAP* **value** *VALUE* 37 | | **bpftool** **map pop** *MAP* 38 | | **bpftool** **map enqueue** *MAP* **value** *VALUE* 39 | | **bpftool** **map dequeue** *MAP* 40 | | **bpftool** **map freeze** *MAP* 41 | | **bpftool** **map help** 42 | | 43 | | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* | **name** *MAP_NAME* } 44 | | *DATA* := { [**hex**] *BYTES* } 45 | | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* | **name** *PROG_NAME* } 46 | | *VALUE* := { *DATA* | *MAP* | *PROG* } 47 | | *UPDATE_FLAGS* := { **any** | **exist** | **noexist** } 48 | | *TYPE* := { **hash** | **array** | **prog_array** | **perf_event_array** | **percpu_hash** 49 | | | **percpu_array** | **stack_trace** | **cgroup_array** | **lru_hash** 50 | | | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps** 51 | | | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash** 52 | | | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** 53 | | | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage** 54 | | **task_storage** | **cred_storage** | **msg_storage** | **ipc_storage** 55 | | **file_storage** } 56 | 57 | DESCRIPTION 58 | =========== 59 | **bpftool map { show | list }** [*MAP*] 60 | Show information about loaded maps. If *MAP* is specified 61 | show information only about given maps, otherwise list all 62 | maps currently loaded on the system. In case of **name**, 63 | *MAP* may match several maps which will all be shown. 64 | 65 | Output will start with map ID followed by map type and 66 | zero or more named attributes (depending on kernel version). 67 | 68 | Since Linux 5.8 bpftool is able to discover information about 69 | processes that hold open file descriptors (FDs) against BPF 70 | maps. On such kernels bpftool will automatically emit this 71 | information as well. 72 | 73 | **bpftool map create** *FILE* **type** *TYPE* **key** *KEY_SIZE* **value** *VALUE_SIZE* **entries** *MAX_ENTRIES* **name** *NAME* [**flags** *FLAGS*] [**inner_map** *MAP*] [**dev** *NAME*] 74 | Create a new map with given parameters and pin it to *bpffs* 75 | as *FILE*. 76 | 77 | *FLAGS* should be an integer which is the combination of 78 | desired flags, e.g. 1024 for **BPF_F_MMAPABLE** (see bpf.h 79 | UAPI header for existing flags). 80 | 81 | To create maps of type array-of-maps or hash-of-maps, the 82 | **inner_map** keyword must be used to pass an inner map. The 83 | kernel needs it to collect metadata related to the inner maps 84 | that the new map will work with. 85 | 86 | Keyword **dev** expects a network interface name, and is used 87 | to request hardware offload for the map. 88 | 89 | **bpftool map dump** *MAP* 90 | Dump all entries in a given *MAP*. In case of **name**, 91 | *MAP* may match several maps which will all be dumped. 92 | 93 | **bpftool map update** *MAP* [**key** *DATA*] [**value** *VALUE*] [*UPDATE_FLAGS*] 94 | Update map entry for a given *KEY*. 95 | 96 | *UPDATE_FLAGS* can be one of: **any** update existing entry 97 | or add if doesn't exit; **exist** update only if entry already 98 | exists; **noexist** update only if entry doesn't exist. 99 | 100 | If the **hex** keyword is provided in front of the bytes 101 | sequence, the bytes are parsed as hexadecimal values, even if 102 | no "0x" prefix is added. If the keyword is not provided, then 103 | the bytes are parsed as decimal values, unless a "0x" prefix 104 | (for hexadecimal) or a "0" prefix (for octal) is provided. 105 | 106 | **bpftool map lookup** *MAP* [**key** *DATA*] 107 | Lookup **key** in the map. 108 | 109 | **bpftool map getnext** *MAP* [**key** *DATA*] 110 | Get next key. If *key* is not specified, get first key. 111 | 112 | **bpftool map delete** *MAP* **key** *DATA* 113 | Remove entry from the map. 114 | 115 | **bpftool map pin** *MAP* *FILE* 116 | Pin map *MAP* as *FILE*. 117 | 118 | Note: *FILE* must be located in *bpffs* mount. It must not 119 | contain a dot character ('.'), which is reserved for future 120 | extensions of *bpffs*. 121 | 122 | **bpftool** **map event_pipe** *MAP* [**cpu** *N* **index** *M*] 123 | Read events from a **BPF_MAP_TYPE_PERF_EVENT_ARRAY** map. 124 | 125 | Install perf rings into a perf event array map and dump 126 | output of any **bpf_perf_event_output**\ () call in the kernel. 127 | By default read the number of CPUs on the system and 128 | install perf ring for each CPU in the corresponding index 129 | in the array. 130 | 131 | If **cpu** and **index** are specified, install perf ring 132 | for given **cpu** at **index** in the array (single ring). 133 | 134 | Note that installing a perf ring into an array will silently 135 | replace any existing ring. Any other application will stop 136 | receiving events if it installed its rings earlier. 137 | 138 | **bpftool map peek** *MAP* 139 | Peek next value in the queue or stack. 140 | 141 | **bpftool map push** *MAP* **value** *VALUE* 142 | Push *VALUE* onto the stack. 143 | 144 | **bpftool map pop** *MAP* 145 | Pop and print value from the stack. 146 | 147 | **bpftool map enqueue** *MAP* **value** *VALUE* 148 | Enqueue *VALUE* into the queue. 149 | 150 | **bpftool map dequeue** *MAP* 151 | Dequeue and print value from the queue. 152 | 153 | **bpftool map freeze** *MAP* 154 | Freeze the map as read-only from user space. Entries from a 155 | frozen map can not longer be updated or deleted with the 156 | **bpf**\ () system call. This operation is not reversible, 157 | and the map remains immutable from user space until its 158 | destruction. However, read and write permissions for BPF 159 | programs to the map remain unchanged. 160 | 161 | **bpftool map help** 162 | Print short help message. 163 | 164 | OPTIONS 165 | ======= 166 | .. include:: common_options.rst 167 | 168 | -f, --bpffs 169 | Show file names of pinned maps. 170 | 171 | -n, --nomount 172 | Do not automatically attempt to mount any virtual file system 173 | (such as tracefs or BPF virtual file system) when necessary. 174 | 175 | EXAMPLES 176 | ======== 177 | **# bpftool map show** 178 | 179 | :: 180 | 181 | 10: hash name some_map flags 0x0 182 | key 4B value 8B max_entries 2048 memlock 167936B 183 | pids systemd(1) 184 | 185 | The following three commands are equivalent: 186 | 187 | | 188 | | **# bpftool map update id 10 key hex 20 c4 b7 00 value hex 0f ff ff ab 01 02 03 4c** 189 | | **# bpftool map update id 10 key 0x20 0xc4 0xb7 0x00 value 0x0f 0xff 0xff 0xab 0x01 0x02 0x03 0x4c** 190 | | **# bpftool map update id 10 key 32 196 183 0 value 15 255 255 171 1 2 3 76** 191 | 192 | **# bpftool map lookup id 10 key 0 1 2 3** 193 | 194 | :: 195 | 196 | key: 00 01 02 03 value: 00 01 02 03 04 05 06 07 197 | 198 | 199 | **# bpftool map dump id 10** 200 | 201 | :: 202 | 203 | key: 00 01 02 03 value: 00 01 02 03 04 05 06 07 204 | key: 0d 00 07 00 value: 02 00 00 00 01 02 03 04 205 | Found 2 elements 206 | 207 | **# bpftool map getnext id 10 key 0 1 2 3** 208 | 209 | :: 210 | 211 | key: 212 | 00 01 02 03 213 | next key: 214 | 0d 00 07 00 215 | 216 | | 217 | | **# mount -t bpf none /sys/fs/bpf/** 218 | | **# bpftool map pin id 10 /sys/fs/bpf/map** 219 | | **# bpftool map del pinned /sys/fs/bpf/map key 13 00 07 00** 220 | 221 | Note that map update can also be used in order to change the program references 222 | hold by a program array map. This can be used, for example, to change the 223 | programs used for tail-call jumps at runtime, without having to reload the 224 | entry-point program. Below is an example for this use case: we load a program 225 | defining a prog array map, and with a main function that contains a tail call 226 | to other programs that can be used either to "process" packets or to "debug" 227 | processing. Note that the prog array map MUST be pinned into the BPF virtual 228 | file system for the map update to work successfully, as kernel flushes prog 229 | array maps when they have no more references from user space (and the update 230 | would be lost as soon as bpftool exits). 231 | 232 | | 233 | | **# bpftool prog loadall tail_calls.o /sys/fs/bpf/foo type xdp** 234 | | **# bpftool prog --bpffs** 235 | 236 | :: 237 | 238 | 545: xdp name main_func tag 674b4b5597193dc3 gpl 239 | loaded_at 2018-12-12T15:02:58+0000 uid 0 240 | xlated 240B jited 257B memlock 4096B map_ids 294 241 | pinned /sys/fs/bpf/foo/xdp 242 | 546: xdp name bpf_func_process tag e369a529024751fc gpl 243 | loaded_at 2018-12-12T15:02:58+0000 uid 0 244 | xlated 200B jited 164B memlock 4096B 245 | pinned /sys/fs/bpf/foo/process 246 | 547: xdp name bpf_func_debug tag 0b597868bc7f0976 gpl 247 | loaded_at 2018-12-12T15:02:58+0000 uid 0 248 | xlated 200B jited 164B memlock 4096B 249 | pinned /sys/fs/bpf/foo/debug 250 | 251 | **# bpftool map** 252 | 253 | :: 254 | 255 | 294: prog_array name jmp_table flags 0x0 256 | key 4B value 4B max_entries 1 memlock 4096B 257 | owner_prog_type xdp owner jited 258 | 259 | | 260 | | **# bpftool map pin id 294 /sys/fs/bpf/bar** 261 | | **# bpftool map dump pinned /sys/fs/bpf/bar** 262 | 263 | :: 264 | 265 | Found 0 elements 266 | 267 | | 268 | | **# bpftool map update pinned /sys/fs/bpf/bar key 0 0 0 0 value pinned /sys/fs/bpf/foo/debug** 269 | | **# bpftool map dump pinned /sys/fs/bpf/bar** 270 | 271 | :: 272 | 273 | key: 00 00 00 00 value: 22 02 00 00 274 | Found 1 element 275 | -------------------------------------------------------------------------------- /tools/bpf/bpftool/map.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 | /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include "json_writer.h" 22 | #include "main.h" 23 | 24 | const char * const map_type_name[] = { 25 | [BPF_MAP_TYPE_UNSPEC] = "unspec", 26 | [BPF_MAP_TYPE_HASH] = "hash", 27 | [BPF_MAP_TYPE_ARRAY] = "array", 28 | [BPF_MAP_TYPE_PROG_ARRAY] = "prog_array", 29 | [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array", 30 | [BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash", 31 | [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array", 32 | [BPF_MAP_TYPE_STACK_TRACE] = "stack_trace", 33 | [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array", 34 | [BPF_MAP_TYPE_LRU_HASH] = "lru_hash", 35 | [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash", 36 | [BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie", 37 | [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps", 38 | [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps", 39 | [BPF_MAP_TYPE_DEVMAP] = "devmap", 40 | [BPF_MAP_TYPE_DEVMAP_HASH] = "devmap_hash", 41 | [BPF_MAP_TYPE_SOCKMAP] = "sockmap", 42 | [BPF_MAP_TYPE_CPUMAP] = "cpumap", 43 | [BPF_MAP_TYPE_XSKMAP] = "xskmap", 44 | [BPF_MAP_TYPE_SOCKHASH] = "sockhash", 45 | [BPF_MAP_TYPE_CGROUP_STORAGE] = "cgroup_storage", 46 | [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray", 47 | [BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE] = "percpu_cgroup_storage", 48 | [BPF_MAP_TYPE_QUEUE] = "queue", 49 | [BPF_MAP_TYPE_STACK] = "stack", 50 | [BPF_MAP_TYPE_SK_STORAGE] = "sk_storage", 51 | [BPF_MAP_TYPE_STRUCT_OPS] = "struct_ops", 52 | [BPF_MAP_TYPE_RINGBUF] = "ringbuf", 53 | [BPF_MAP_TYPE_INODE_STORAGE] = "inode_storage", 54 | [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage", 55 | [BPF_MAP_TYPE_CRED_STORAGE] = "cred_storage", 56 | [BPF_MAP_TYPE_MSG_STORAGE] = "msg_storage", 57 | [BPF_MAP_TYPE_IPC_STORAGE] = "ipc_storage", 58 | [BPF_MAP_TYPE_FILE_STORAGE] = "file_storage", 59 | }; 60 | 61 | const size_t map_type_name_size = ARRAY_SIZE(map_type_name); 62 | 63 | static bool map_is_per_cpu(__u32 type) 64 | { 65 | return type == BPF_MAP_TYPE_PERCPU_HASH || 66 | type == BPF_MAP_TYPE_PERCPU_ARRAY || 67 | type == BPF_MAP_TYPE_LRU_PERCPU_HASH || 68 | type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE; 69 | } 70 | 71 | static bool map_is_map_of_maps(__u32 type) 72 | { 73 | return type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 74 | type == BPF_MAP_TYPE_HASH_OF_MAPS; 75 | } 76 | 77 | static bool map_is_map_of_progs(__u32 type) 78 | { 79 | return type == BPF_MAP_TYPE_PROG_ARRAY; 80 | } 81 | 82 | static int map_type_from_str(const char *type) 83 | { 84 | unsigned int i; 85 | 86 | for (i = 0; i < ARRAY_SIZE(map_type_name); i++) 87 | /* Don't allow prefixing in case of possible future shadowing */ 88 | if (map_type_name[i] && !strcmp(map_type_name[i], type)) 89 | return i; 90 | return -1; 91 | } 92 | 93 | static void *alloc_value(struct bpf_map_info *info) 94 | { 95 | if (map_is_per_cpu(info->type)) 96 | return malloc(round_up(info->value_size, 8) * 97 | get_possible_cpus()); 98 | else 99 | return malloc(info->value_size); 100 | } 101 | 102 | static int do_dump_btf(const struct btf_dumper *d, 103 | struct bpf_map_info *map_info, void *key, 104 | void *value) 105 | { 106 | __u32 value_id; 107 | int ret; 108 | 109 | /* start of key-value pair */ 110 | jsonw_start_object(d->jw); 111 | 112 | if (map_info->btf_key_type_id) { 113 | jsonw_name(d->jw, "key"); 114 | 115 | ret = btf_dumper_type(d, map_info->btf_key_type_id, key); 116 | if (ret) 117 | goto err_end_obj; 118 | } 119 | 120 | value_id = map_info->btf_vmlinux_value_type_id ? 121 | : map_info->btf_value_type_id; 122 | 123 | if (!map_is_per_cpu(map_info->type)) { 124 | jsonw_name(d->jw, "value"); 125 | ret = btf_dumper_type(d, value_id, value); 126 | } else { 127 | unsigned int i, n, step; 128 | 129 | jsonw_name(d->jw, "values"); 130 | jsonw_start_array(d->jw); 131 | n = get_possible_cpus(); 132 | step = round_up(map_info->value_size, 8); 133 | for (i = 0; i < n; i++) { 134 | jsonw_start_object(d->jw); 135 | jsonw_int_field(d->jw, "cpu", i); 136 | jsonw_name(d->jw, "value"); 137 | ret = btf_dumper_type(d, value_id, value + i * step); 138 | jsonw_end_object(d->jw); 139 | if (ret) 140 | break; 141 | } 142 | jsonw_end_array(d->jw); 143 | } 144 | 145 | err_end_obj: 146 | /* end of key-value pair */ 147 | jsonw_end_object(d->jw); 148 | 149 | return ret; 150 | } 151 | 152 | static json_writer_t *get_btf_writer(void) 153 | { 154 | json_writer_t *jw = jsonw_new(stdout); 155 | 156 | if (!jw) 157 | return NULL; 158 | jsonw_pretty(jw, true); 159 | 160 | return jw; 161 | } 162 | 163 | static void print_entry_json(struct bpf_map_info *info, unsigned char *key, 164 | unsigned char *value, struct btf *btf) 165 | { 166 | jsonw_start_object(json_wtr); 167 | 168 | if (!map_is_per_cpu(info->type)) { 169 | jsonw_name(json_wtr, "key"); 170 | print_hex_data_json(key, info->key_size); 171 | jsonw_name(json_wtr, "value"); 172 | print_hex_data_json(value, info->value_size); 173 | if (btf) { 174 | struct btf_dumper d = { 175 | .btf = btf, 176 | .jw = json_wtr, 177 | .is_plain_text = false, 178 | }; 179 | 180 | jsonw_name(json_wtr, "formatted"); 181 | do_dump_btf(&d, info, key, value); 182 | } 183 | } else { 184 | unsigned int i, n, step; 185 | 186 | n = get_possible_cpus(); 187 | step = round_up(info->value_size, 8); 188 | 189 | jsonw_name(json_wtr, "key"); 190 | print_hex_data_json(key, info->key_size); 191 | 192 | jsonw_name(json_wtr, "values"); 193 | jsonw_start_array(json_wtr); 194 | for (i = 0; i < n; i++) { 195 | jsonw_start_object(json_wtr); 196 | 197 | jsonw_int_field(json_wtr, "cpu", i); 198 | 199 | jsonw_name(json_wtr, "value"); 200 | print_hex_data_json(value + i * step, 201 | info->value_size); 202 | 203 | jsonw_end_object(json_wtr); 204 | } 205 | jsonw_end_array(json_wtr); 206 | if (btf) { 207 | struct btf_dumper d = { 208 | .btf = btf, 209 | .jw = json_wtr, 210 | .is_plain_text = false, 211 | }; 212 | 213 | jsonw_name(json_wtr, "formatted"); 214 | do_dump_btf(&d, info, key, value); 215 | } 216 | } 217 | 218 | jsonw_end_object(json_wtr); 219 | } 220 | 221 | static void 222 | print_entry_error_msg(struct bpf_map_info *info, unsigned char *key, 223 | const char *error_msg) 224 | { 225 | int msg_size = strlen(error_msg); 226 | bool single_line, break_names; 227 | 228 | break_names = info->key_size > 16 || msg_size > 16; 229 | single_line = info->key_size + msg_size <= 24 && !break_names; 230 | 231 | printf("key:%c", break_names ? '\n' : ' '); 232 | fprint_hex(stdout, key, info->key_size, " "); 233 | 234 | printf(single_line ? " " : "\n"); 235 | 236 | printf("value:%c%s", break_names ? '\n' : ' ', error_msg); 237 | 238 | printf("\n"); 239 | } 240 | 241 | static void 242 | print_entry_error(struct bpf_map_info *map_info, void *key, int lookup_errno) 243 | { 244 | /* For prog_array maps or arrays of maps, failure to lookup the value 245 | * means there is no entry for that key. Do not print an error message 246 | * in that case. 247 | */ 248 | if ((map_is_map_of_maps(map_info->type) || 249 | map_is_map_of_progs(map_info->type)) && lookup_errno == ENOENT) 250 | return; 251 | 252 | if (json_output) { 253 | jsonw_start_object(json_wtr); /* entry */ 254 | jsonw_name(json_wtr, "key"); 255 | print_hex_data_json(key, map_info->key_size); 256 | jsonw_name(json_wtr, "value"); 257 | jsonw_start_object(json_wtr); /* error */ 258 | jsonw_string_field(json_wtr, "error", strerror(lookup_errno)); 259 | jsonw_end_object(json_wtr); /* error */ 260 | jsonw_end_object(json_wtr); /* entry */ 261 | } else { 262 | const char *msg = NULL; 263 | 264 | if (lookup_errno == ENOENT) 265 | msg = ""; 266 | else if (lookup_errno == ENOSPC && 267 | map_info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) 268 | msg = ""; 269 | 270 | print_entry_error_msg(map_info, key, 271 | msg ? : strerror(lookup_errno)); 272 | } 273 | } 274 | 275 | static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, 276 | unsigned char *value) 277 | { 278 | if (!map_is_per_cpu(info->type)) { 279 | bool single_line, break_names; 280 | 281 | break_names = info->key_size > 16 || info->value_size > 16; 282 | single_line = info->key_size + info->value_size <= 24 && 283 | !break_names; 284 | 285 | if (info->key_size) { 286 | printf("key:%c", break_names ? '\n' : ' '); 287 | fprint_hex(stdout, key, info->key_size, " "); 288 | 289 | printf(single_line ? " " : "\n"); 290 | } 291 | 292 | if (info->value_size) { 293 | printf("value:%c", break_names ? '\n' : ' '); 294 | fprint_hex(stdout, value, info->value_size, " "); 295 | } 296 | 297 | printf("\n"); 298 | } else { 299 | unsigned int i, n, step; 300 | 301 | n = get_possible_cpus(); 302 | step = round_up(info->value_size, 8); 303 | 304 | if (info->key_size) { 305 | printf("key:\n"); 306 | fprint_hex(stdout, key, info->key_size, " "); 307 | printf("\n"); 308 | } 309 | if (info->value_size) { 310 | for (i = 0; i < n; i++) { 311 | printf("value (CPU %02d):%c", 312 | i, info->value_size > 16 ? '\n' : ' '); 313 | fprint_hex(stdout, value + i * step, 314 | info->value_size, " "); 315 | printf("\n"); 316 | } 317 | } 318 | } 319 | } 320 | 321 | static char **parse_bytes(char **argv, const char *name, unsigned char *val, 322 | unsigned int n) 323 | { 324 | unsigned int i = 0, base = 0; 325 | char *endptr; 326 | 327 | if (is_prefix(*argv, "hex")) { 328 | base = 16; 329 | argv++; 330 | } 331 | 332 | while (i < n && argv[i]) { 333 | val[i] = strtoul(argv[i], &endptr, base); 334 | if (*endptr) { 335 | p_err("error parsing byte: %s", argv[i]); 336 | return NULL; 337 | } 338 | i++; 339 | } 340 | 341 | if (i != n) { 342 | p_err("%s expected %d bytes got %d", name, n, i); 343 | return NULL; 344 | } 345 | 346 | return argv + i; 347 | } 348 | 349 | /* on per cpu maps we must copy the provided value on all value instances */ 350 | static void fill_per_cpu_value(struct bpf_map_info *info, void *value) 351 | { 352 | unsigned int i, n, step; 353 | 354 | if (!map_is_per_cpu(info->type)) 355 | return; 356 | 357 | n = get_possible_cpus(); 358 | step = round_up(info->value_size, 8); 359 | for (i = 1; i < n; i++) 360 | memcpy(value + i * step, value, info->value_size); 361 | } 362 | 363 | static int parse_elem(char **argv, struct bpf_map_info *info, 364 | void *key, void *value, __u32 key_size, __u32 value_size, 365 | __u32 *flags, __u32 **value_fd) 366 | { 367 | if (!*argv) { 368 | if (!key && !value) 369 | return 0; 370 | p_err("did not find %s", key ? "key" : "value"); 371 | return -1; 372 | } 373 | 374 | if (is_prefix(*argv, "key")) { 375 | if (!key) { 376 | if (key_size) 377 | p_err("duplicate key"); 378 | else 379 | p_err("unnecessary key"); 380 | return -1; 381 | } 382 | 383 | argv = parse_bytes(argv + 1, "key", key, key_size); 384 | if (!argv) 385 | return -1; 386 | 387 | return parse_elem(argv, info, NULL, value, key_size, value_size, 388 | flags, value_fd); 389 | } else if (is_prefix(*argv, "value")) { 390 | int fd; 391 | 392 | if (!value) { 393 | if (value_size) 394 | p_err("duplicate value"); 395 | else 396 | p_err("unnecessary value"); 397 | return -1; 398 | } 399 | 400 | argv++; 401 | 402 | if (map_is_map_of_maps(info->type)) { 403 | int argc = 2; 404 | 405 | if (value_size != 4) { 406 | p_err("value smaller than 4B for map in map?"); 407 | return -1; 408 | } 409 | if (!argv[0] || !argv[1]) { 410 | p_err("not enough value arguments for map in map"); 411 | return -1; 412 | } 413 | 414 | fd = map_parse_fd(&argc, &argv); 415 | if (fd < 0) 416 | return -1; 417 | 418 | *value_fd = value; 419 | **value_fd = fd; 420 | } else if (map_is_map_of_progs(info->type)) { 421 | int argc = 2; 422 | 423 | if (value_size != 4) { 424 | p_err("value smaller than 4B for map of progs?"); 425 | return -1; 426 | } 427 | if (!argv[0] || !argv[1]) { 428 | p_err("not enough value arguments for map of progs"); 429 | return -1; 430 | } 431 | if (is_prefix(*argv, "id")) 432 | p_info("Warning: updating program array via MAP_ID, make sure this map is kept open\n" 433 | " by some process or pinned otherwise update will be lost"); 434 | 435 | fd = prog_parse_fd(&argc, &argv); 436 | if (fd < 0) 437 | return -1; 438 | 439 | *value_fd = value; 440 | **value_fd = fd; 441 | } else { 442 | argv = parse_bytes(argv, "value", value, value_size); 443 | if (!argv) 444 | return -1; 445 | 446 | fill_per_cpu_value(info, value); 447 | } 448 | 449 | return parse_elem(argv, info, key, NULL, key_size, value_size, 450 | flags, NULL); 451 | } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || 452 | is_prefix(*argv, "exist")) { 453 | if (!flags) { 454 | p_err("flags specified multiple times: %s", *argv); 455 | return -1; 456 | } 457 | 458 | if (is_prefix(*argv, "any")) 459 | *flags = BPF_ANY; 460 | else if (is_prefix(*argv, "noexist")) 461 | *flags = BPF_NOEXIST; 462 | else if (is_prefix(*argv, "exist")) 463 | *flags = BPF_EXIST; 464 | 465 | return parse_elem(argv + 1, info, key, value, key_size, 466 | value_size, NULL, value_fd); 467 | } 468 | 469 | p_err("expected key or value, got: %s", *argv); 470 | return -1; 471 | } 472 | 473 | static void show_map_header_json(struct bpf_map_info *info, json_writer_t *wtr) 474 | { 475 | jsonw_uint_field(wtr, "id", info->id); 476 | if (info->type < ARRAY_SIZE(map_type_name)) 477 | jsonw_string_field(wtr, "type", map_type_name[info->type]); 478 | else 479 | jsonw_uint_field(wtr, "type", info->type); 480 | 481 | if (*info->name) 482 | jsonw_string_field(wtr, "name", info->name); 483 | 484 | jsonw_name(wtr, "flags"); 485 | jsonw_printf(wtr, "%d", info->map_flags); 486 | } 487 | 488 | static int show_map_close_json(int fd, struct bpf_map_info *info) 489 | { 490 | char *memlock, *frozen_str; 491 | int frozen = 0; 492 | 493 | memlock = get_fdinfo(fd, "memlock"); 494 | frozen_str = get_fdinfo(fd, "frozen"); 495 | 496 | jsonw_start_object(json_wtr); 497 | 498 | show_map_header_json(info, json_wtr); 499 | 500 | print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 501 | 502 | jsonw_uint_field(json_wtr, "bytes_key", info->key_size); 503 | jsonw_uint_field(json_wtr, "bytes_value", info->value_size); 504 | jsonw_uint_field(json_wtr, "max_entries", info->max_entries); 505 | 506 | if (memlock) 507 | jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 508 | free(memlock); 509 | 510 | if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { 511 | char *owner_prog_type = get_fdinfo(fd, "owner_prog_type"); 512 | char *owner_jited = get_fdinfo(fd, "owner_jited"); 513 | 514 | if (owner_prog_type) { 515 | unsigned int prog_type = atoi(owner_prog_type); 516 | 517 | if (prog_type < prog_type_name_size) 518 | jsonw_string_field(json_wtr, "owner_prog_type", 519 | prog_type_name[prog_type]); 520 | else 521 | jsonw_uint_field(json_wtr, "owner_prog_type", 522 | prog_type); 523 | } 524 | if (owner_jited) 525 | jsonw_bool_field(json_wtr, "owner_jited", 526 | !!atoi(owner_jited)); 527 | 528 | free(owner_prog_type); 529 | free(owner_jited); 530 | } 531 | close(fd); 532 | 533 | if (frozen_str) { 534 | frozen = atoi(frozen_str); 535 | free(frozen_str); 536 | } 537 | jsonw_int_field(json_wtr, "frozen", frozen); 538 | 539 | if (info->btf_id) 540 | jsonw_int_field(json_wtr, "btf_id", info->btf_id); 541 | 542 | if (!hash_empty(map_table.table)) { 543 | struct pinned_obj *obj; 544 | 545 | jsonw_name(json_wtr, "pinned"); 546 | jsonw_start_array(json_wtr); 547 | hash_for_each_possible(map_table.table, obj, hash, info->id) { 548 | if (obj->id == info->id) 549 | jsonw_string(json_wtr, obj->path); 550 | } 551 | jsonw_end_array(json_wtr); 552 | } 553 | 554 | emit_obj_refs_json(&refs_table, info->id, json_wtr); 555 | 556 | jsonw_end_object(json_wtr); 557 | 558 | return 0; 559 | } 560 | 561 | static void show_map_header_plain(struct bpf_map_info *info) 562 | { 563 | printf("%u: ", info->id); 564 | if (info->type < ARRAY_SIZE(map_type_name)) 565 | printf("%s ", map_type_name[info->type]); 566 | else 567 | printf("type %u ", info->type); 568 | 569 | if (*info->name) 570 | printf("name %s ", info->name); 571 | 572 | printf("flags 0x%x", info->map_flags); 573 | print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 574 | printf("\n"); 575 | } 576 | 577 | static int show_map_close_plain(int fd, struct bpf_map_info *info) 578 | { 579 | char *memlock, *frozen_str; 580 | int frozen = 0; 581 | 582 | memlock = get_fdinfo(fd, "memlock"); 583 | frozen_str = get_fdinfo(fd, "frozen"); 584 | 585 | show_map_header_plain(info); 586 | printf("\tkey %uB value %uB max_entries %u", 587 | info->key_size, info->value_size, info->max_entries); 588 | 589 | if (memlock) 590 | printf(" memlock %sB", memlock); 591 | free(memlock); 592 | 593 | if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { 594 | char *owner_prog_type = get_fdinfo(fd, "owner_prog_type"); 595 | char *owner_jited = get_fdinfo(fd, "owner_jited"); 596 | 597 | if (owner_prog_type || owner_jited) 598 | printf("\n\t"); 599 | if (owner_prog_type) { 600 | unsigned int prog_type = atoi(owner_prog_type); 601 | 602 | if (prog_type < prog_type_name_size) 603 | printf("owner_prog_type %s ", 604 | prog_type_name[prog_type]); 605 | else 606 | printf("owner_prog_type %d ", prog_type); 607 | } 608 | if (owner_jited) 609 | printf("owner%s jited", 610 | atoi(owner_jited) ? "" : " not"); 611 | 612 | free(owner_prog_type); 613 | free(owner_jited); 614 | } 615 | close(fd); 616 | 617 | if (!hash_empty(map_table.table)) { 618 | struct pinned_obj *obj; 619 | 620 | hash_for_each_possible(map_table.table, obj, hash, info->id) { 621 | if (obj->id == info->id) 622 | printf("\n\tpinned %s", obj->path); 623 | } 624 | } 625 | printf("\n"); 626 | 627 | if (frozen_str) { 628 | frozen = atoi(frozen_str); 629 | free(frozen_str); 630 | } 631 | 632 | if (!info->btf_id && !frozen) 633 | return 0; 634 | 635 | printf("\t"); 636 | 637 | if (info->btf_id) 638 | printf("btf_id %d", info->btf_id); 639 | 640 | if (frozen) 641 | printf("%sfrozen", info->btf_id ? " " : ""); 642 | 643 | emit_obj_refs_plain(&refs_table, info->id, "\n\tpids "); 644 | 645 | printf("\n"); 646 | return 0; 647 | } 648 | 649 | static int do_show_subset(int argc, char **argv) 650 | { 651 | struct bpf_map_info info = {}; 652 | __u32 len = sizeof(info); 653 | int *fds = NULL; 654 | int nb_fds, i; 655 | int err = -1; 656 | 657 | fds = malloc(sizeof(int)); 658 | if (!fds) { 659 | p_err("mem alloc failed"); 660 | return -1; 661 | } 662 | nb_fds = map_parse_fds(&argc, &argv, &fds); 663 | if (nb_fds < 1) 664 | goto exit_free; 665 | 666 | if (json_output && nb_fds > 1) 667 | jsonw_start_array(json_wtr); /* root array */ 668 | for (i = 0; i < nb_fds; i++) { 669 | err = bpf_obj_get_info_by_fd(fds[i], &info, &len); 670 | if (err) { 671 | p_err("can't get map info: %s", 672 | strerror(errno)); 673 | for (; i < nb_fds; i++) 674 | close(fds[i]); 675 | break; 676 | } 677 | 678 | if (json_output) 679 | show_map_close_json(fds[i], &info); 680 | else 681 | show_map_close_plain(fds[i], &info); 682 | 683 | close(fds[i]); 684 | } 685 | if (json_output && nb_fds > 1) 686 | jsonw_end_array(json_wtr); /* root array */ 687 | 688 | exit_free: 689 | free(fds); 690 | return err; 691 | } 692 | 693 | static int do_show(int argc, char **argv) 694 | { 695 | struct bpf_map_info info = {}; 696 | __u32 len = sizeof(info); 697 | __u32 id = 0; 698 | int err; 699 | int fd; 700 | 701 | if (show_pinned) 702 | build_pinned_obj_table(&map_table, BPF_OBJ_MAP); 703 | build_obj_refs_table(&refs_table, BPF_OBJ_MAP); 704 | 705 | if (argc == 2) 706 | return do_show_subset(argc, argv); 707 | 708 | if (argc) 709 | return BAD_ARG(); 710 | 711 | if (json_output) 712 | jsonw_start_array(json_wtr); 713 | while (true) { 714 | err = bpf_map_get_next_id(id, &id); 715 | if (err) { 716 | if (errno == ENOENT) 717 | break; 718 | p_err("can't get next map: %s%s", strerror(errno), 719 | errno == EINVAL ? " -- kernel too old?" : ""); 720 | break; 721 | } 722 | 723 | fd = bpf_map_get_fd_by_id(id); 724 | if (fd < 0) { 725 | if (errno == ENOENT) 726 | continue; 727 | p_err("can't get map by id (%u): %s", 728 | id, strerror(errno)); 729 | break; 730 | } 731 | 732 | err = bpf_obj_get_info_by_fd(fd, &info, &len); 733 | if (err) { 734 | p_err("can't get map info: %s", strerror(errno)); 735 | close(fd); 736 | break; 737 | } 738 | 739 | if (json_output) 740 | show_map_close_json(fd, &info); 741 | else 742 | show_map_close_plain(fd, &info); 743 | } 744 | if (json_output) 745 | jsonw_end_array(json_wtr); 746 | 747 | delete_obj_refs_table(&refs_table); 748 | 749 | return errno == ENOENT ? 0 : -1; 750 | } 751 | 752 | static int dump_map_elem(int fd, void *key, void *value, 753 | struct bpf_map_info *map_info, struct btf *btf, 754 | json_writer_t *btf_wtr) 755 | { 756 | if (bpf_map_lookup_elem(fd, key, value)) { 757 | print_entry_error(map_info, key, errno); 758 | return -1; 759 | } 760 | 761 | if (json_output) { 762 | print_entry_json(map_info, key, value, btf); 763 | } else if (btf) { 764 | struct btf_dumper d = { 765 | .btf = btf, 766 | .jw = btf_wtr, 767 | .is_plain_text = true, 768 | }; 769 | 770 | do_dump_btf(&d, map_info, key, value); 771 | } else { 772 | print_entry_plain(map_info, key, value); 773 | } 774 | 775 | return 0; 776 | } 777 | 778 | static int maps_have_btf(int *fds, int nb_fds) 779 | { 780 | struct bpf_map_info info = {}; 781 | __u32 len = sizeof(info); 782 | int err, i; 783 | 784 | for (i = 0; i < nb_fds; i++) { 785 | err = bpf_obj_get_info_by_fd(fds[i], &info, &len); 786 | if (err) { 787 | p_err("can't get map info: %s", strerror(errno)); 788 | return -1; 789 | } 790 | 791 | if (!info.btf_id) 792 | return 0; 793 | } 794 | 795 | return 1; 796 | } 797 | 798 | static struct btf *btf_vmlinux; 799 | 800 | static struct btf *get_map_kv_btf(const struct bpf_map_info *info) 801 | { 802 | struct btf *btf = NULL; 803 | 804 | if (info->btf_vmlinux_value_type_id) { 805 | if (!btf_vmlinux) { 806 | btf_vmlinux = libbpf_find_kernel_btf(); 807 | if (IS_ERR(btf_vmlinux)) 808 | p_err("failed to get kernel btf"); 809 | } 810 | return btf_vmlinux; 811 | } else if (info->btf_value_type_id) { 812 | int err; 813 | 814 | err = btf__get_from_id(info->btf_id, &btf); 815 | if (err || !btf) { 816 | p_err("failed to get btf"); 817 | btf = err ? ERR_PTR(err) : ERR_PTR(-ESRCH); 818 | } 819 | } 820 | 821 | return btf; 822 | } 823 | 824 | static void free_map_kv_btf(struct btf *btf) 825 | { 826 | if (!IS_ERR(btf) && btf != btf_vmlinux) 827 | btf__free(btf); 828 | } 829 | 830 | static void free_btf_vmlinux(void) 831 | { 832 | if (!IS_ERR(btf_vmlinux)) 833 | btf__free(btf_vmlinux); 834 | } 835 | 836 | static int 837 | map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, 838 | bool show_header) 839 | { 840 | void *key, *value, *prev_key; 841 | unsigned int num_elems = 0; 842 | struct btf *btf = NULL; 843 | int err; 844 | 845 | key = malloc(info->key_size); 846 | value = alloc_value(info); 847 | if (!key || !value) { 848 | p_err("mem alloc failed"); 849 | err = -1; 850 | goto exit_free; 851 | } 852 | 853 | prev_key = NULL; 854 | 855 | if (wtr) { 856 | btf = get_map_kv_btf(info); 857 | if (IS_ERR(btf)) { 858 | err = PTR_ERR(btf); 859 | goto exit_free; 860 | } 861 | 862 | if (show_header) { 863 | jsonw_start_object(wtr); /* map object */ 864 | show_map_header_json(info, wtr); 865 | jsonw_name(wtr, "elements"); 866 | } 867 | jsonw_start_array(wtr); /* elements */ 868 | } else if (show_header) { 869 | show_map_header_plain(info); 870 | } 871 | 872 | if (info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY && 873 | info->value_size != 8) 874 | p_info("Warning: cannot read values from %s map with value_size != 8", 875 | map_type_name[info->type]); 876 | while (true) { 877 | err = bpf_map_get_next_key(fd, prev_key, key); 878 | if (err) { 879 | if (errno == ENOENT) 880 | err = 0; 881 | break; 882 | } 883 | if (!dump_map_elem(fd, key, value, info, btf, wtr)) 884 | num_elems++; 885 | prev_key = key; 886 | } 887 | 888 | if (wtr) { 889 | jsonw_end_array(wtr); /* elements */ 890 | if (show_header) 891 | jsonw_end_object(wtr); /* map object */ 892 | } else { 893 | printf("Found %u element%s\n", num_elems, 894 | num_elems != 1 ? "s" : ""); 895 | } 896 | 897 | exit_free: 898 | free(key); 899 | free(value); 900 | close(fd); 901 | free_map_kv_btf(btf); 902 | 903 | return err; 904 | } 905 | 906 | static int do_dump(int argc, char **argv) 907 | { 908 | json_writer_t *wtr = NULL, *btf_wtr = NULL; 909 | struct bpf_map_info info = {}; 910 | int nb_fds, i = 0; 911 | __u32 len = sizeof(info); 912 | int *fds = NULL; 913 | int err = -1; 914 | 915 | if (argc != 2) 916 | usage(); 917 | 918 | fds = malloc(sizeof(int)); 919 | if (!fds) { 920 | p_err("mem alloc failed"); 921 | return -1; 922 | } 923 | nb_fds = map_parse_fds(&argc, &argv, &fds); 924 | if (nb_fds < 1) 925 | goto exit_free; 926 | 927 | if (json_output) { 928 | wtr = json_wtr; 929 | } else { 930 | int do_plain_btf; 931 | 932 | do_plain_btf = maps_have_btf(fds, nb_fds); 933 | if (do_plain_btf < 0) 934 | goto exit_close; 935 | 936 | if (do_plain_btf) { 937 | btf_wtr = get_btf_writer(); 938 | wtr = btf_wtr; 939 | if (!btf_wtr) 940 | p_info("failed to create json writer for btf. falling back to plain output"); 941 | } 942 | } 943 | 944 | if (wtr && nb_fds > 1) 945 | jsonw_start_array(wtr); /* root array */ 946 | for (i = 0; i < nb_fds; i++) { 947 | if (bpf_obj_get_info_by_fd(fds[i], &info, &len)) { 948 | p_err("can't get map info: %s", strerror(errno)); 949 | break; 950 | } 951 | err = map_dump(fds[i], &info, wtr, nb_fds > 1); 952 | if (!wtr && i != nb_fds - 1) 953 | printf("\n"); 954 | 955 | if (err) 956 | break; 957 | close(fds[i]); 958 | } 959 | if (wtr && nb_fds > 1) 960 | jsonw_end_array(wtr); /* root array */ 961 | 962 | if (btf_wtr) 963 | jsonw_destroy(&btf_wtr); 964 | exit_close: 965 | for (; i < nb_fds; i++) 966 | close(fds[i]); 967 | exit_free: 968 | free(fds); 969 | free_btf_vmlinux(); 970 | return err; 971 | } 972 | 973 | static int alloc_key_value(struct bpf_map_info *info, void **key, void **value) 974 | { 975 | *key = NULL; 976 | *value = NULL; 977 | 978 | if (info->key_size) { 979 | *key = malloc(info->key_size); 980 | if (!*key) { 981 | p_err("key mem alloc failed"); 982 | return -1; 983 | } 984 | } 985 | 986 | if (info->value_size) { 987 | *value = alloc_value(info); 988 | if (!*value) { 989 | p_err("value mem alloc failed"); 990 | free(*key); 991 | *key = NULL; 992 | return -1; 993 | } 994 | } 995 | 996 | return 0; 997 | } 998 | 999 | static int do_update(int argc, char **argv) 1000 | { 1001 | struct bpf_map_info info = {}; 1002 | __u32 len = sizeof(info); 1003 | __u32 *value_fd = NULL; 1004 | __u32 flags = BPF_ANY; 1005 | void *key, *value; 1006 | int fd, err; 1007 | 1008 | if (argc < 2) 1009 | usage(); 1010 | 1011 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1012 | if (fd < 0) 1013 | return -1; 1014 | 1015 | err = alloc_key_value(&info, &key, &value); 1016 | if (err) 1017 | goto exit_free; 1018 | 1019 | err = parse_elem(argv, &info, key, value, info.key_size, 1020 | info.value_size, &flags, &value_fd); 1021 | if (err) 1022 | goto exit_free; 1023 | 1024 | err = bpf_map_update_elem(fd, key, value, flags); 1025 | if (err) { 1026 | p_err("update failed: %s", strerror(errno)); 1027 | goto exit_free; 1028 | } 1029 | 1030 | exit_free: 1031 | if (value_fd) 1032 | close(*value_fd); 1033 | free(key); 1034 | free(value); 1035 | close(fd); 1036 | 1037 | if (!err && json_output) 1038 | jsonw_null(json_wtr); 1039 | return err; 1040 | } 1041 | 1042 | static void print_key_value(struct bpf_map_info *info, void *key, 1043 | void *value) 1044 | { 1045 | json_writer_t *btf_wtr; 1046 | struct btf *btf = NULL; 1047 | int err; 1048 | 1049 | err = btf__get_from_id(info->btf_id, &btf); 1050 | if (err) { 1051 | p_err("failed to get btf"); 1052 | return; 1053 | } 1054 | 1055 | if (json_output) { 1056 | print_entry_json(info, key, value, btf); 1057 | } else if (btf) { 1058 | /* if here json_wtr wouldn't have been initialised, 1059 | * so let's create separate writer for btf 1060 | */ 1061 | btf_wtr = get_btf_writer(); 1062 | if (!btf_wtr) { 1063 | p_info("failed to create json writer for btf. falling back to plain output"); 1064 | btf__free(btf); 1065 | btf = NULL; 1066 | print_entry_plain(info, key, value); 1067 | } else { 1068 | struct btf_dumper d = { 1069 | .btf = btf, 1070 | .jw = btf_wtr, 1071 | .is_plain_text = true, 1072 | }; 1073 | 1074 | do_dump_btf(&d, info, key, value); 1075 | jsonw_destroy(&btf_wtr); 1076 | } 1077 | } else { 1078 | print_entry_plain(info, key, value); 1079 | } 1080 | btf__free(btf); 1081 | } 1082 | 1083 | static int do_lookup(int argc, char **argv) 1084 | { 1085 | struct bpf_map_info info = {}; 1086 | __u32 len = sizeof(info); 1087 | void *key, *value; 1088 | int err; 1089 | int fd; 1090 | 1091 | if (argc < 2) 1092 | usage(); 1093 | 1094 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1095 | if (fd < 0) 1096 | return -1; 1097 | 1098 | err = alloc_key_value(&info, &key, &value); 1099 | if (err) 1100 | goto exit_free; 1101 | 1102 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 1103 | if (err) 1104 | goto exit_free; 1105 | 1106 | err = bpf_map_lookup_elem(fd, key, value); 1107 | if (err) { 1108 | if (errno == ENOENT) { 1109 | if (json_output) { 1110 | jsonw_null(json_wtr); 1111 | } else { 1112 | printf("key:\n"); 1113 | fprint_hex(stdout, key, info.key_size, " "); 1114 | printf("\n\nNot found\n"); 1115 | } 1116 | } else { 1117 | p_err("lookup failed: %s", strerror(errno)); 1118 | } 1119 | 1120 | goto exit_free; 1121 | } 1122 | 1123 | /* here means bpf_map_lookup_elem() succeeded */ 1124 | print_key_value(&info, key, value); 1125 | 1126 | exit_free: 1127 | free(key); 1128 | free(value); 1129 | close(fd); 1130 | 1131 | return err; 1132 | } 1133 | 1134 | static int do_getnext(int argc, char **argv) 1135 | { 1136 | struct bpf_map_info info = {}; 1137 | __u32 len = sizeof(info); 1138 | void *key, *nextkey; 1139 | int err; 1140 | int fd; 1141 | 1142 | if (argc < 2) 1143 | usage(); 1144 | 1145 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1146 | if (fd < 0) 1147 | return -1; 1148 | 1149 | key = malloc(info.key_size); 1150 | nextkey = malloc(info.key_size); 1151 | if (!key || !nextkey) { 1152 | p_err("mem alloc failed"); 1153 | err = -1; 1154 | goto exit_free; 1155 | } 1156 | 1157 | if (argc) { 1158 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, 1159 | NULL, NULL); 1160 | if (err) 1161 | goto exit_free; 1162 | } else { 1163 | free(key); 1164 | key = NULL; 1165 | } 1166 | 1167 | err = bpf_map_get_next_key(fd, key, nextkey); 1168 | if (err) { 1169 | p_err("can't get next key: %s", strerror(errno)); 1170 | goto exit_free; 1171 | } 1172 | 1173 | if (json_output) { 1174 | jsonw_start_object(json_wtr); 1175 | if (key) { 1176 | jsonw_name(json_wtr, "key"); 1177 | print_hex_data_json(key, info.key_size); 1178 | } else { 1179 | jsonw_null_field(json_wtr, "key"); 1180 | } 1181 | jsonw_name(json_wtr, "next_key"); 1182 | print_hex_data_json(nextkey, info.key_size); 1183 | jsonw_end_object(json_wtr); 1184 | } else { 1185 | if (key) { 1186 | printf("key:\n"); 1187 | fprint_hex(stdout, key, info.key_size, " "); 1188 | printf("\n"); 1189 | } else { 1190 | printf("key: None\n"); 1191 | } 1192 | printf("next key:\n"); 1193 | fprint_hex(stdout, nextkey, info.key_size, " "); 1194 | printf("\n"); 1195 | } 1196 | 1197 | exit_free: 1198 | free(nextkey); 1199 | free(key); 1200 | close(fd); 1201 | 1202 | return err; 1203 | } 1204 | 1205 | static int do_delete(int argc, char **argv) 1206 | { 1207 | struct bpf_map_info info = {}; 1208 | __u32 len = sizeof(info); 1209 | void *key; 1210 | int err; 1211 | int fd; 1212 | 1213 | if (argc < 2) 1214 | usage(); 1215 | 1216 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1217 | if (fd < 0) 1218 | return -1; 1219 | 1220 | key = malloc(info.key_size); 1221 | if (!key) { 1222 | p_err("mem alloc failed"); 1223 | err = -1; 1224 | goto exit_free; 1225 | } 1226 | 1227 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); 1228 | if (err) 1229 | goto exit_free; 1230 | 1231 | err = bpf_map_delete_elem(fd, key); 1232 | if (err) 1233 | p_err("delete failed: %s", strerror(errno)); 1234 | 1235 | exit_free: 1236 | free(key); 1237 | close(fd); 1238 | 1239 | if (!err && json_output) 1240 | jsonw_null(json_wtr); 1241 | return err; 1242 | } 1243 | 1244 | static int do_pin(int argc, char **argv) 1245 | { 1246 | int err; 1247 | 1248 | err = do_pin_any(argc, argv, map_parse_fd); 1249 | if (!err && json_output) 1250 | jsonw_null(json_wtr); 1251 | return err; 1252 | } 1253 | 1254 | static int do_create(int argc, char **argv) 1255 | { 1256 | struct bpf_create_map_attr attr = { NULL, }; 1257 | const char *pinfile; 1258 | int err = -1, fd; 1259 | 1260 | if (!REQ_ARGS(7)) 1261 | return -1; 1262 | pinfile = GET_ARG(); 1263 | 1264 | while (argc) { 1265 | if (!REQ_ARGS(2)) 1266 | return -1; 1267 | 1268 | if (is_prefix(*argv, "type")) { 1269 | NEXT_ARG(); 1270 | 1271 | if (attr.map_type) { 1272 | p_err("map type already specified"); 1273 | goto exit; 1274 | } 1275 | 1276 | attr.map_type = map_type_from_str(*argv); 1277 | if ((int)attr.map_type < 0) { 1278 | p_err("unrecognized map type: %s", *argv); 1279 | goto exit; 1280 | } 1281 | NEXT_ARG(); 1282 | } else if (is_prefix(*argv, "name")) { 1283 | NEXT_ARG(); 1284 | attr.name = GET_ARG(); 1285 | } else if (is_prefix(*argv, "key")) { 1286 | if (parse_u32_arg(&argc, &argv, &attr.key_size, 1287 | "key size")) 1288 | goto exit; 1289 | } else if (is_prefix(*argv, "value")) { 1290 | if (parse_u32_arg(&argc, &argv, &attr.value_size, 1291 | "value size")) 1292 | goto exit; 1293 | } else if (is_prefix(*argv, "entries")) { 1294 | if (parse_u32_arg(&argc, &argv, &attr.max_entries, 1295 | "max entries")) 1296 | goto exit; 1297 | } else if (is_prefix(*argv, "flags")) { 1298 | if (parse_u32_arg(&argc, &argv, &attr.map_flags, 1299 | "flags")) 1300 | goto exit; 1301 | } else if (is_prefix(*argv, "dev")) { 1302 | NEXT_ARG(); 1303 | 1304 | if (attr.map_ifindex) { 1305 | p_err("offload device already specified"); 1306 | goto exit; 1307 | } 1308 | 1309 | attr.map_ifindex = if_nametoindex(*argv); 1310 | if (!attr.map_ifindex) { 1311 | p_err("unrecognized netdevice '%s': %s", 1312 | *argv, strerror(errno)); 1313 | goto exit; 1314 | } 1315 | NEXT_ARG(); 1316 | } else if (is_prefix(*argv, "inner_map")) { 1317 | struct bpf_map_info info = {}; 1318 | __u32 len = sizeof(info); 1319 | int inner_map_fd; 1320 | 1321 | NEXT_ARG(); 1322 | if (!REQ_ARGS(2)) 1323 | usage(); 1324 | inner_map_fd = map_parse_fd_and_info(&argc, &argv, 1325 | &info, &len); 1326 | if (inner_map_fd < 0) 1327 | return -1; 1328 | attr.inner_map_fd = inner_map_fd; 1329 | } else { 1330 | p_err("unknown arg %s", *argv); 1331 | goto exit; 1332 | } 1333 | } 1334 | 1335 | if (!attr.name) { 1336 | p_err("map name not specified"); 1337 | goto exit; 1338 | } 1339 | 1340 | set_max_rlimit(); 1341 | 1342 | fd = bpf_create_map_xattr(&attr); 1343 | if (fd < 0) { 1344 | p_err("map create failed: %s", strerror(errno)); 1345 | goto exit; 1346 | } 1347 | 1348 | err = do_pin_fd(fd, pinfile); 1349 | close(fd); 1350 | if (err) 1351 | goto exit; 1352 | 1353 | if (json_output) 1354 | jsonw_null(json_wtr); 1355 | 1356 | exit: 1357 | if (attr.inner_map_fd > 0) 1358 | close(attr.inner_map_fd); 1359 | 1360 | return err; 1361 | } 1362 | 1363 | static int do_pop_dequeue(int argc, char **argv) 1364 | { 1365 | struct bpf_map_info info = {}; 1366 | __u32 len = sizeof(info); 1367 | void *key, *value; 1368 | int err; 1369 | int fd; 1370 | 1371 | if (argc < 2) 1372 | usage(); 1373 | 1374 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 1375 | if (fd < 0) 1376 | return -1; 1377 | 1378 | err = alloc_key_value(&info, &key, &value); 1379 | if (err) 1380 | goto exit_free; 1381 | 1382 | err = bpf_map_lookup_and_delete_elem(fd, key, value); 1383 | if (err) { 1384 | if (errno == ENOENT) { 1385 | if (json_output) 1386 | jsonw_null(json_wtr); 1387 | else 1388 | printf("Error: empty map\n"); 1389 | } else { 1390 | p_err("pop failed: %s", strerror(errno)); 1391 | } 1392 | 1393 | goto exit_free; 1394 | } 1395 | 1396 | print_key_value(&info, key, value); 1397 | 1398 | exit_free: 1399 | free(key); 1400 | free(value); 1401 | close(fd); 1402 | 1403 | return err; 1404 | } 1405 | 1406 | static int do_freeze(int argc, char **argv) 1407 | { 1408 | int err, fd; 1409 | 1410 | if (!REQ_ARGS(2)) 1411 | return -1; 1412 | 1413 | fd = map_parse_fd(&argc, &argv); 1414 | if (fd < 0) 1415 | return -1; 1416 | 1417 | if (argc) { 1418 | close(fd); 1419 | return BAD_ARG(); 1420 | } 1421 | 1422 | err = bpf_map_freeze(fd); 1423 | close(fd); 1424 | if (err) { 1425 | p_err("failed to freeze map: %s", strerror(errno)); 1426 | return err; 1427 | } 1428 | 1429 | if (json_output) 1430 | jsonw_null(json_wtr); 1431 | 1432 | return 0; 1433 | } 1434 | 1435 | static int do_help(int argc, char **argv) 1436 | { 1437 | if (json_output) { 1438 | jsonw_null(json_wtr); 1439 | return 0; 1440 | } 1441 | 1442 | fprintf(stderr, 1443 | "Usage: %1$s %2$s { show | list } [MAP]\n" 1444 | " %1$s %2$s create FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n" 1445 | " entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" 1446 | " [inner_map MAP] [dev NAME]\n" 1447 | " %1$s %2$s dump MAP\n" 1448 | " %1$s %2$s update MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n" 1449 | " %1$s %2$s lookup MAP [key DATA]\n" 1450 | " %1$s %2$s getnext MAP [key DATA]\n" 1451 | " %1$s %2$s delete MAP key DATA\n" 1452 | " %1$s %2$s pin MAP FILE\n" 1453 | " %1$s %2$s event_pipe MAP [cpu N index M]\n" 1454 | " %1$s %2$s peek MAP\n" 1455 | " %1$s %2$s push MAP value VALUE\n" 1456 | " %1$s %2$s pop MAP\n" 1457 | " %1$s %2$s enqueue MAP value VALUE\n" 1458 | " %1$s %2$s dequeue MAP\n" 1459 | " %1$s %2$s freeze MAP\n" 1460 | " %1$s %2$s help\n" 1461 | "\n" 1462 | " " HELP_SPEC_MAP "\n" 1463 | " DATA := { [hex] BYTES }\n" 1464 | " " HELP_SPEC_PROGRAM "\n" 1465 | " VALUE := { DATA | MAP | PROG }\n" 1466 | " UPDATE_FLAGS := { any | exist | noexist }\n" 1467 | " TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n" 1468 | " percpu_array | stack_trace | cgroup_array | lru_hash |\n" 1469 | " lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n" 1470 | " devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n" 1471 | " cgroup_storage | reuseport_sockarray | percpu_cgroup_storage |\n" 1472 | " queue | stack | sk_storage | struct_ops | ringbuf | inode_storage |\n" 1473 | " task_storage | cred_storage | msg_storage | ipc_storage |\n" 1474 | " file_storage }\n" 1475 | " " HELP_SPEC_OPTIONS "\n" 1476 | "", 1477 | bin_name, argv[-2]); 1478 | 1479 | return 0; 1480 | } 1481 | 1482 | static const struct cmd cmds[] = { 1483 | { "show", do_show }, 1484 | { "list", do_show }, 1485 | { "help", do_help }, 1486 | { "dump", do_dump }, 1487 | { "update", do_update }, 1488 | { "lookup", do_lookup }, 1489 | { "getnext", do_getnext }, 1490 | { "delete", do_delete }, 1491 | { "pin", do_pin }, 1492 | { "event_pipe", do_event_pipe }, 1493 | { "create", do_create }, 1494 | { "peek", do_lookup }, 1495 | { "push", do_update }, 1496 | { "enqueue", do_update }, 1497 | { "pop", do_pop_dequeue }, 1498 | { "dequeue", do_pop_dequeue }, 1499 | { "freeze", do_freeze }, 1500 | { 0 } 1501 | }; 1502 | 1503 | int do_map(int argc, char **argv) 1504 | { 1505 | return cmd_select(cmds, argc, argv, do_help); 1506 | } 1507 | -------------------------------------------------------------------------------- /tools/lib/bpf/libbpf_probes.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2019 Netronome Systems, Inc. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "bpf.h" 17 | #include "libbpf.h" 18 | #include "libbpf_internal.h" 19 | 20 | static bool grep(const char *buffer, const char *pattern) 21 | { 22 | return !!strstr(buffer, pattern); 23 | } 24 | 25 | static int get_vendor_id(int ifindex) 26 | { 27 | char ifname[IF_NAMESIZE], path[64], buf[8]; 28 | ssize_t len; 29 | int fd; 30 | 31 | if (!if_indextoname(ifindex, ifname)) 32 | return -1; 33 | 34 | snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname); 35 | 36 | fd = open(path, O_RDONLY); 37 | if (fd < 0) 38 | return -1; 39 | 40 | len = read(fd, buf, sizeof(buf)); 41 | close(fd); 42 | if (len < 0) 43 | return -1; 44 | if (len >= (ssize_t)sizeof(buf)) 45 | return -1; 46 | buf[len] = '\0'; 47 | 48 | return strtol(buf, NULL, 0); 49 | } 50 | 51 | static int get_kernel_version(void) 52 | { 53 | int version, subversion, patchlevel; 54 | struct utsname utsn; 55 | 56 | /* Return 0 on failure, and attempt to probe with empty kversion */ 57 | if (uname(&utsn)) 58 | return 0; 59 | 60 | if (sscanf(utsn.release, "%d.%d.%d", 61 | &version, &subversion, &patchlevel) != 3) 62 | return 0; 63 | 64 | return (version << 16) + (subversion << 8) + patchlevel; 65 | } 66 | 67 | static void 68 | probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, 69 | size_t insns_cnt, char *buf, size_t buf_len, __u32 ifindex) 70 | { 71 | struct bpf_load_program_attr xattr = {}; 72 | int fd; 73 | 74 | switch (prog_type) { 75 | case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: 76 | xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT; 77 | break; 78 | case BPF_PROG_TYPE_SK_LOOKUP: 79 | xattr.expected_attach_type = BPF_SK_LOOKUP; 80 | break; 81 | case BPF_PROG_TYPE_KPROBE: 82 | xattr.kern_version = get_kernel_version(); 83 | break; 84 | case BPF_PROG_TYPE_UNSPEC: 85 | case BPF_PROG_TYPE_SOCKET_FILTER: 86 | case BPF_PROG_TYPE_SCHED_CLS: 87 | case BPF_PROG_TYPE_SCHED_ACT: 88 | case BPF_PROG_TYPE_TRACEPOINT: 89 | case BPF_PROG_TYPE_XDP: 90 | case BPF_PROG_TYPE_PERF_EVENT: 91 | case BPF_PROG_TYPE_CGROUP_SKB: 92 | case BPF_PROG_TYPE_CGROUP_SOCK: 93 | case BPF_PROG_TYPE_LWT_IN: 94 | case BPF_PROG_TYPE_LWT_OUT: 95 | case BPF_PROG_TYPE_LWT_XMIT: 96 | case BPF_PROG_TYPE_SOCK_OPS: 97 | case BPF_PROG_TYPE_SK_SKB: 98 | case BPF_PROG_TYPE_CGROUP_DEVICE: 99 | case BPF_PROG_TYPE_SK_MSG: 100 | case BPF_PROG_TYPE_RAW_TRACEPOINT: 101 | case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE: 102 | case BPF_PROG_TYPE_LWT_SEG6LOCAL: 103 | case BPF_PROG_TYPE_LIRC_MODE2: 104 | case BPF_PROG_TYPE_SK_REUSEPORT: 105 | case BPF_PROG_TYPE_FLOW_DISSECTOR: 106 | case BPF_PROG_TYPE_CGROUP_SYSCTL: 107 | case BPF_PROG_TYPE_CGROUP_SOCKOPT: 108 | case BPF_PROG_TYPE_TRACING: 109 | case BPF_PROG_TYPE_STRUCT_OPS: 110 | case BPF_PROG_TYPE_EXT: 111 | case BPF_PROG_TYPE_LSM: 112 | default: 113 | break; 114 | } 115 | 116 | xattr.prog_type = prog_type; 117 | xattr.insns = insns; 118 | xattr.insns_cnt = insns_cnt; 119 | xattr.license = "GPL"; 120 | xattr.prog_ifindex = ifindex; 121 | 122 | fd = bpf_load_program_xattr(&xattr, buf, buf_len); 123 | if (fd >= 0) 124 | close(fd); 125 | } 126 | 127 | bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex) 128 | { 129 | struct bpf_insn insns[2] = { 130 | BPF_MOV64_IMM(BPF_REG_0, 0), 131 | BPF_EXIT_INSN() 132 | }; 133 | 134 | if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS) 135 | /* nfp returns -EINVAL on exit(0) with TC offload */ 136 | insns[0].imm = 2; 137 | 138 | errno = 0; 139 | probe_load(prog_type, insns, ARRAY_SIZE(insns), NULL, 0, ifindex); 140 | 141 | return errno != EINVAL && errno != EOPNOTSUPP; 142 | } 143 | 144 | int libbpf__load_raw_btf(const char *raw_types, size_t types_len, 145 | const char *str_sec, size_t str_len) 146 | { 147 | struct btf_header hdr = { 148 | .magic = BTF_MAGIC, 149 | .version = BTF_VERSION, 150 | .hdr_len = sizeof(struct btf_header), 151 | .type_len = types_len, 152 | .str_off = types_len, 153 | .str_len = str_len, 154 | }; 155 | int btf_fd, btf_len; 156 | __u8 *raw_btf; 157 | 158 | btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len; 159 | raw_btf = malloc(btf_len); 160 | if (!raw_btf) 161 | return -ENOMEM; 162 | 163 | memcpy(raw_btf, &hdr, sizeof(hdr)); 164 | memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len); 165 | memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len); 166 | 167 | btf_fd = bpf_load_btf(raw_btf, btf_len, NULL, 0, false); 168 | 169 | free(raw_btf); 170 | return btf_fd; 171 | } 172 | 173 | static int load_local_storage_btf(void) 174 | { 175 | const char strs[] = "\0bpf_spin_lock\0val\0cnt\0l"; 176 | /* struct bpf_spin_lock { 177 | * int val; 178 | * }; 179 | * struct val { 180 | * int cnt; 181 | * struct bpf_spin_lock l; 182 | * }; 183 | */ 184 | __u32 types[] = { 185 | /* int */ 186 | BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ 187 | /* struct bpf_spin_lock */ /* [2] */ 188 | BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4), 189 | BTF_MEMBER_ENC(15, 1, 0), /* int val; */ 190 | /* struct val */ /* [3] */ 191 | BTF_TYPE_ENC(15, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 8), 192 | BTF_MEMBER_ENC(19, 1, 0), /* int cnt; */ 193 | BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */ 194 | }; 195 | 196 | return libbpf__load_raw_btf((char *)types, sizeof(types), 197 | strs, sizeof(strs)); 198 | } 199 | 200 | bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex) 201 | { 202 | int key_size, value_size, max_entries, map_flags; 203 | __u32 btf_key_type_id = 0, btf_value_type_id = 0; 204 | struct bpf_create_map_attr attr = {}; 205 | int fd = -1, btf_fd = -1, fd_inner; 206 | 207 | key_size = sizeof(__u32); 208 | value_size = sizeof(__u32); 209 | max_entries = 1; 210 | map_flags = 0; 211 | 212 | switch (map_type) { 213 | case BPF_MAP_TYPE_STACK_TRACE: 214 | value_size = sizeof(__u64); 215 | break; 216 | case BPF_MAP_TYPE_LPM_TRIE: 217 | key_size = sizeof(__u64); 218 | value_size = sizeof(__u64); 219 | map_flags = BPF_F_NO_PREALLOC; 220 | break; 221 | case BPF_MAP_TYPE_CGROUP_STORAGE: 222 | case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: 223 | key_size = sizeof(struct bpf_cgroup_storage_key); 224 | value_size = sizeof(__u64); 225 | max_entries = 0; 226 | break; 227 | case BPF_MAP_TYPE_QUEUE: 228 | case BPF_MAP_TYPE_STACK: 229 | key_size = 0; 230 | break; 231 | case BPF_MAP_TYPE_SK_STORAGE: 232 | case BPF_MAP_TYPE_INODE_STORAGE: 233 | case BPF_MAP_TYPE_TASK_STORAGE: 234 | case BPF_MAP_TYPE_CRED_STORAGE: 235 | case BPF_MAP_TYPE_MSG_STORAGE: 236 | case BPF_MAP_TYPE_IPC_STORAGE: 237 | case BPF_MAP_TYPE_FILE_STORAGE: 238 | btf_key_type_id = 1; 239 | btf_value_type_id = 3; 240 | value_size = 8; 241 | max_entries = 0; 242 | map_flags = BPF_F_NO_PREALLOC; 243 | btf_fd = load_local_storage_btf(); 244 | if (btf_fd < 0) 245 | return false; 246 | break; 247 | case BPF_MAP_TYPE_RINGBUF: 248 | key_size = 0; 249 | value_size = 0; 250 | max_entries = 4096; 251 | break; 252 | case BPF_MAP_TYPE_UNSPEC: 253 | case BPF_MAP_TYPE_HASH: 254 | case BPF_MAP_TYPE_ARRAY: 255 | case BPF_MAP_TYPE_PROG_ARRAY: 256 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: 257 | case BPF_MAP_TYPE_PERCPU_HASH: 258 | case BPF_MAP_TYPE_PERCPU_ARRAY: 259 | case BPF_MAP_TYPE_CGROUP_ARRAY: 260 | case BPF_MAP_TYPE_LRU_HASH: 261 | case BPF_MAP_TYPE_LRU_PERCPU_HASH: 262 | case BPF_MAP_TYPE_ARRAY_OF_MAPS: 263 | case BPF_MAP_TYPE_HASH_OF_MAPS: 264 | case BPF_MAP_TYPE_DEVMAP: 265 | case BPF_MAP_TYPE_DEVMAP_HASH: 266 | case BPF_MAP_TYPE_SOCKMAP: 267 | case BPF_MAP_TYPE_CPUMAP: 268 | case BPF_MAP_TYPE_XSKMAP: 269 | case BPF_MAP_TYPE_SOCKHASH: 270 | case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: 271 | case BPF_MAP_TYPE_STRUCT_OPS: 272 | default: 273 | break; 274 | } 275 | 276 | if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 277 | map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { 278 | /* TODO: probe for device, once libbpf has a function to create 279 | * map-in-map for offload 280 | */ 281 | if (ifindex) 282 | return false; 283 | 284 | fd_inner = bpf_create_map(BPF_MAP_TYPE_HASH, 285 | sizeof(__u32), sizeof(__u32), 1, 0); 286 | if (fd_inner < 0) 287 | return false; 288 | fd = bpf_create_map_in_map(map_type, NULL, sizeof(__u32), 289 | fd_inner, 1, 0); 290 | close(fd_inner); 291 | } else { 292 | /* Note: No other restriction on map type probes for offload */ 293 | attr.map_type = map_type; 294 | attr.key_size = key_size; 295 | attr.value_size = value_size; 296 | attr.max_entries = max_entries; 297 | attr.map_flags = map_flags; 298 | attr.map_ifindex = ifindex; 299 | if (btf_fd >= 0) { 300 | attr.btf_fd = btf_fd; 301 | attr.btf_key_type_id = btf_key_type_id; 302 | attr.btf_value_type_id = btf_value_type_id; 303 | } 304 | 305 | fd = bpf_create_map_xattr(&attr); 306 | } 307 | if (fd >= 0) 308 | close(fd); 309 | if (btf_fd >= 0) 310 | close(btf_fd); 311 | 312 | return fd >= 0; 313 | } 314 | 315 | bool bpf_probe_helper(enum bpf_func_id id, enum bpf_prog_type prog_type, 316 | __u32 ifindex) 317 | { 318 | struct bpf_insn insns[2] = { 319 | BPF_EMIT_CALL(id), 320 | BPF_EXIT_INSN() 321 | }; 322 | char buf[4096] = {}; 323 | bool res; 324 | 325 | probe_load(prog_type, insns, ARRAY_SIZE(insns), buf, sizeof(buf), 326 | ifindex); 327 | res = !grep(buf, "invalid func ") && !grep(buf, "unknown func "); 328 | 329 | if (ifindex) { 330 | switch (get_vendor_id(ifindex)) { 331 | case 0x19ee: /* Netronome specific */ 332 | res = res && !grep(buf, "not supported by FW") && 333 | !grep(buf, "unsupported function id"); 334 | break; 335 | default: 336 | break; 337 | } 338 | } 339 | 340 | return res; 341 | } 342 | 343 | /* 344 | * Probe for availability of kernel commit (5.3): 345 | * 346 | * c04c0d2b968a ("bpf: increase complexity limit and maximum program size") 347 | */ 348 | bool bpf_probe_large_insn_limit(__u32 ifindex) 349 | { 350 | struct bpf_insn insns[BPF_MAXINSNS + 1]; 351 | int i; 352 | 353 | for (i = 0; i < BPF_MAXINSNS; i++) 354 | insns[i] = BPF_MOV64_IMM(BPF_REG_0, 1); 355 | insns[BPF_MAXINSNS] = BPF_EXIT_INSN(); 356 | 357 | errno = 0; 358 | probe_load(BPF_PROG_TYPE_SCHED_CLS, insns, ARRAY_SIZE(insns), NULL, 0, 359 | ifindex); 360 | 361 | return errno != E2BIG && errno != EINVAL; 362 | } 363 | -------------------------------------------------------------------------------- /tools/testing/selftests/bpf/prog_tests/test_local_storage.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* 4 | * Copyright (C) 2020 Google LLC. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "local_storage.skel.h" 13 | #include "network_helpers.h" 14 | 15 | #ifndef __NR_pidfd_open 16 | #define __NR_pidfd_open 434 17 | #endif 18 | 19 | static inline int sys_pidfd_open(pid_t pid, unsigned int flags) 20 | { 21 | return syscall(__NR_pidfd_open, pid, flags); 22 | } 23 | 24 | static unsigned int duration; 25 | 26 | #define TEST_STORAGE_VALUE 0xbeefdead 27 | 28 | struct storage { 29 | void *inode; 30 | unsigned int value; 31 | /* Lock ensures that spin locked versions of local stoage operations 32 | * also work, most operations in this tests are still single threaded 33 | */ 34 | struct bpf_spin_lock lock; 35 | }; 36 | 37 | /* Fork and exec the provided rm binary and return the exit code of the 38 | * forked process and its pid. 39 | */ 40 | static int run_self_unlink(int *monitored_pid, const char *rm_path) 41 | { 42 | int child_pid, child_status, ret; 43 | int null_fd; 44 | 45 | child_pid = fork(); 46 | if (child_pid == 0) { 47 | null_fd = open("/dev/null", O_WRONLY); 48 | dup2(null_fd, STDOUT_FILENO); 49 | dup2(null_fd, STDERR_FILENO); 50 | close(null_fd); 51 | 52 | *monitored_pid = getpid(); 53 | /* Use the copied /usr/bin/rm to delete itself 54 | * /tmp/copy_of_rm /tmp/copy_of_rm. 55 | */ 56 | ret = execlp(rm_path, rm_path, rm_path, NULL); 57 | if (ret) 58 | exit(errno); 59 | } else if (child_pid > 0) { 60 | waitpid(child_pid, &child_status, 0); 61 | return WEXITSTATUS(child_status); 62 | } 63 | 64 | return -EINVAL; 65 | } 66 | 67 | static bool check_syscall_operations(int map_fd, int obj_fd) 68 | { 69 | struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } }, 70 | lookup_val = { .value = 0, .lock = { 0 } }; 71 | int err; 72 | 73 | /* Looking up an existing element should fail initially */ 74 | err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val, 75 | BPF_F_LOCK); 76 | if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem", 77 | "err:%d errno:%d\n", err, errno)) 78 | return false; 79 | 80 | /* Create a new element */ 81 | err = bpf_map_update_elem(map_fd, &obj_fd, &val, 82 | BPF_NOEXIST | BPF_F_LOCK); 83 | if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err, 84 | errno)) 85 | return false; 86 | 87 | /* Lookup the newly created element */ 88 | err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val, 89 | BPF_F_LOCK); 90 | if (CHECK(err < 0, "bpf_map_lookup_elem", "err:%d errno:%d", err, 91 | errno)) 92 | return false; 93 | 94 | /* Check the value of the newly created element */ 95 | if (CHECK(lookup_val.value != val.value, "bpf_map_lookup_elem", 96 | "value got = %x errno:%d", lookup_val.value, val.value)) 97 | return false; 98 | 99 | err = bpf_map_delete_elem(map_fd, &obj_fd); 100 | if (CHECK(err, "bpf_map_delete_elem()", "err:%d errno:%d\n", err, 101 | errno)) 102 | return false; 103 | 104 | /* The lookup should fail, now that the element has been deleted */ 105 | err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val, 106 | BPF_F_LOCK); 107 | if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem", 108 | "err:%d errno:%d\n", err, errno)) 109 | return false; 110 | 111 | return true; 112 | } 113 | 114 | void test_test_local_storage(void) 115 | { 116 | char tmp_dir_path[64] = "/tmp/local_storageXXXXXX"; 117 | int err, serv_sk = -1, task_fd = -1, rm_fd = -1; 118 | struct local_storage *skel = NULL; 119 | char tmp_exec_path[64]; 120 | char cmd[256]; 121 | 122 | skel = local_storage__open_and_load(); 123 | if (CHECK(!skel, "skel_load", "lsm skeleton failed\n")) 124 | goto close_prog; 125 | 126 | err = local_storage__attach(skel); 127 | if (CHECK(err, "attach", "lsm attach failed: %d\n", err)) 128 | goto close_prog; 129 | 130 | task_fd = sys_pidfd_open(getpid(), 0); 131 | if (CHECK(task_fd < 0, "pidfd_open", 132 | "failed to get pidfd err:%d, errno:%d", task_fd, errno)) 133 | goto close_prog; 134 | 135 | if (!check_syscall_operations(bpf_map__fd(skel->maps.task_storage_map), 136 | task_fd)) 137 | goto close_prog; 138 | 139 | /* systopia contrib start */ 140 | if (!check_syscall_operations(bpf_map__fd(skel->maps.cred_storage_map), 141 | task_fd)) 142 | goto close_prog; 143 | /* systopia contrib end */ 144 | 145 | if (CHECK(!mkdtemp(tmp_dir_path), "mkdtemp", 146 | "unable to create tmpdir: %d\n", errno)) 147 | goto close_prog; 148 | 149 | snprintf(tmp_exec_path, sizeof(tmp_exec_path), "%s/copy_of_rm", 150 | tmp_dir_path); 151 | snprintf(cmd, sizeof(cmd), "cp /bin/rm %s", tmp_exec_path); 152 | if (CHECK_FAIL(system(cmd))) 153 | goto close_prog_rmdir; 154 | 155 | rm_fd = open(tmp_exec_path, O_RDONLY); 156 | if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d", 157 | tmp_exec_path, rm_fd, errno)) 158 | goto close_prog_rmdir; 159 | 160 | if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map), 161 | rm_fd)) 162 | goto close_prog_rmdir; 163 | 164 | /* Sets skel->bss->monitored_pid to the pid of the forked child 165 | * forks a child process that executes tmp_exec_path and tries to 166 | * unlink its executable. This operation should be denied by the loaded 167 | * LSM program. 168 | */ 169 | err = run_self_unlink(&skel->bss->monitored_pid, tmp_exec_path); 170 | if (CHECK(err != EPERM, "run_self_unlink", "err %d want EPERM\n", err)) 171 | goto close_prog_rmdir; 172 | 173 | /* Set the process being monitored to be the current process */ 174 | skel->bss->monitored_pid = getpid(); 175 | 176 | /* Move copy_of_rm to a new location so that it triggers the 177 | * inode_rename LSM hook with a new_dentry that has a NULL inode ptr. 178 | */ 179 | snprintf(cmd, sizeof(cmd), "mv %s/copy_of_rm %s/check_null_ptr", 180 | tmp_dir_path, tmp_dir_path); 181 | if (CHECK_FAIL(system(cmd))) 182 | goto close_prog_rmdir; 183 | 184 | CHECK(skel->data->inode_storage_result != 0, "inode_storage_result", 185 | "inode_local_storage not set\n"); 186 | 187 | serv_sk = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0); 188 | if (CHECK(serv_sk < 0, "start_server", "failed to start server\n")) 189 | goto close_prog_rmdir; 190 | 191 | CHECK(skel->data->sk_storage_result != 0, "sk_storage_result", 192 | "sk_local_storage not set\n"); 193 | 194 | if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map), 195 | serv_sk)) 196 | goto close_prog_rmdir; 197 | 198 | close_prog_rmdir: 199 | snprintf(cmd, sizeof(cmd), "rm -rf %s", tmp_dir_path); 200 | system(cmd); 201 | close_prog: 202 | close(serv_sk); 203 | close(rm_fd); 204 | close(task_fd); 205 | local_storage__destroy(skel); 206 | } 207 | -------------------------------------------------------------------------------- /tools/testing/selftests/bpf/progs/local_storage.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* 4 | * Copyright 2020 Google LLC. 5 | */ 6 | 7 | #include "vmlinux.h" 8 | #include 9 | #include 10 | #include 11 | 12 | char _license[] SEC("license") = "GPL"; 13 | 14 | #define DUMMY_STORAGE_VALUE 0xdeadbeef 15 | 16 | int monitored_pid = 0; 17 | int inode_storage_result = -1; 18 | int sk_storage_result = -1; 19 | 20 | struct local_storage { 21 | struct inode *exec_inode; 22 | __u32 value; 23 | struct bpf_spin_lock lock; 24 | }; 25 | 26 | struct { 27 | __uint(type, BPF_MAP_TYPE_INODE_STORAGE); 28 | __uint(map_flags, BPF_F_NO_PREALLOC); 29 | __type(key, int); 30 | __type(value, struct local_storage); 31 | } inode_storage_map SEC(".maps"); 32 | 33 | struct { 34 | __uint(type, BPF_MAP_TYPE_SK_STORAGE); 35 | __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE); 36 | __type(key, int); 37 | __type(value, struct local_storage); 38 | } sk_storage_map SEC(".maps"); 39 | 40 | struct { 41 | __uint(type, BPF_MAP_TYPE_TASK_STORAGE); 42 | __uint(map_flags, BPF_F_NO_PREALLOC); 43 | __type(key, int); 44 | __type(value, struct local_storage); 45 | } task_storage_map SEC(".maps"); 46 | 47 | /* systopia contrib start */ 48 | struct { 49 | __uint(type, BPF_MAP_TYPE_CRED_STORAGE); 50 | __uint(map_flags, BPF_F_NO_PREALLOC); 51 | __type(key, int); 52 | __type(value, struct local_storage); 53 | } cred_storage_map SEC(".maps"); 54 | /* systopia contrib end */ 55 | 56 | SEC("lsm/inode_unlink") 57 | int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim) 58 | { 59 | __u32 pid = bpf_get_current_pid_tgid() >> 32; 60 | struct local_storage *storage; 61 | bool is_self_unlink; 62 | 63 | if (pid != monitored_pid) 64 | return 0; 65 | 66 | storage = bpf_task_storage_get(&task_storage_map, 67 | bpf_get_current_task_btf(), 0, 0); 68 | if (storage) { 69 | /* Don't let an executable delete itself */ 70 | bpf_spin_lock(&storage->lock); 71 | is_self_unlink = storage->exec_inode == victim->d_inode; 72 | bpf_spin_unlock(&storage->lock); 73 | if (is_self_unlink) 74 | return -EPERM; 75 | } 76 | 77 | return 0; 78 | } 79 | 80 | SEC("lsm/inode_rename") 81 | int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry, 82 | struct inode *new_dir, struct dentry *new_dentry, 83 | unsigned int flags) 84 | { 85 | __u32 pid = bpf_get_current_pid_tgid() >> 32; 86 | struct local_storage *storage; 87 | int err; 88 | 89 | /* new_dentry->d_inode can be NULL when the inode is renamed to a file 90 | * that did not exist before. The helper should be able to handle this 91 | * NULL pointer. 92 | */ 93 | bpf_inode_storage_get(&inode_storage_map, new_dentry->d_inode, 0, 94 | BPF_LOCAL_STORAGE_GET_F_CREATE); 95 | 96 | storage = bpf_inode_storage_get(&inode_storage_map, old_dentry->d_inode, 97 | 0, 0); 98 | if (!storage) 99 | return 0; 100 | 101 | bpf_spin_lock(&storage->lock); 102 | if (storage->value != DUMMY_STORAGE_VALUE) 103 | inode_storage_result = -1; 104 | bpf_spin_unlock(&storage->lock); 105 | 106 | err = bpf_inode_storage_delete(&inode_storage_map, old_dentry->d_inode); 107 | if (!err) 108 | inode_storage_result = err; 109 | 110 | return 0; 111 | } 112 | 113 | SEC("lsm/socket_bind") 114 | int BPF_PROG(socket_bind, struct socket *sock, struct sockaddr *address, 115 | int addrlen) 116 | { 117 | __u32 pid = bpf_get_current_pid_tgid() >> 32; 118 | struct local_storage *storage; 119 | int err; 120 | 121 | if (pid != monitored_pid) 122 | return 0; 123 | 124 | storage = bpf_sk_storage_get(&sk_storage_map, sock->sk, 0, 125 | BPF_LOCAL_STORAGE_GET_F_CREATE); 126 | if (!storage) 127 | return 0; 128 | 129 | bpf_spin_lock(&storage->lock); 130 | if (storage->value != DUMMY_STORAGE_VALUE) 131 | sk_storage_result = -1; 132 | bpf_spin_unlock(&storage->lock); 133 | 134 | err = bpf_sk_storage_delete(&sk_storage_map, sock->sk); 135 | if (!err) 136 | sk_storage_result = err; 137 | 138 | return 0; 139 | } 140 | 141 | SEC("lsm/socket_post_create") 142 | int BPF_PROG(socket_post_create, struct socket *sock, int family, int type, 143 | int protocol, int kern) 144 | { 145 | __u32 pid = bpf_get_current_pid_tgid() >> 32; 146 | struct local_storage *storage; 147 | 148 | if (pid != monitored_pid) 149 | return 0; 150 | 151 | storage = bpf_sk_storage_get(&sk_storage_map, sock->sk, 0, 152 | BPF_LOCAL_STORAGE_GET_F_CREATE); 153 | if (!storage) 154 | return 0; 155 | 156 | bpf_spin_lock(&storage->lock); 157 | storage->value = DUMMY_STORAGE_VALUE; 158 | bpf_spin_unlock(&storage->lock); 159 | 160 | return 0; 161 | } 162 | 163 | /* This uses the local storage to remember the inode of the binary that a 164 | * process was originally executing. 165 | */ 166 | SEC("lsm/bprm_committed_creds") 167 | void BPF_PROG(exec, struct linux_binprm *bprm) 168 | { 169 | __u32 pid = bpf_get_current_pid_tgid() >> 32; 170 | struct local_storage *storage; 171 | 172 | if (pid != monitored_pid) 173 | return; 174 | 175 | storage = bpf_task_storage_get(&task_storage_map, 176 | bpf_get_current_task_btf(), 0, 177 | BPF_LOCAL_STORAGE_GET_F_CREATE); 178 | if (storage) { 179 | bpf_spin_lock(&storage->lock); 180 | storage->exec_inode = bprm->file->f_inode; 181 | bpf_spin_unlock(&storage->lock); 182 | } 183 | 184 | storage = bpf_inode_storage_get(&inode_storage_map, bprm->file->f_inode, 185 | 0, BPF_LOCAL_STORAGE_GET_F_CREATE); 186 | if (!storage) 187 | return; 188 | 189 | bpf_spin_lock(&storage->lock); 190 | storage->value = DUMMY_STORAGE_VALUE; 191 | bpf_spin_unlock(&storage->lock); 192 | } 193 | 194 | /* systopia contrib start */ 195 | SEC("lsm/cred_prepare") 196 | int BPF_PROG(cred_prepare, struct cred *new, const struct cred *old, gfp_t gfp) 197 | { 198 | __u32 pid = bpf_get_current_pid_tgid() >> 32; 199 | struct local_storage *storage; 200 | 201 | if (pid != monitored_pid) 202 | return 0; 203 | 204 | storage = bpf_cred_storage_get(&cred_storage_map, new, 0, BPF_LOCAL_STORAGE_GET_F_CREATE); 205 | 206 | if (!storage) 207 | return 0; 208 | 209 | bpf_spin_lock(&storage->lock); 210 | storage->value = DUMMY_STORAGE_VALUE; 211 | bpf_spin_unlock(&storage->lock); 212 | 213 | return 0; 214 | } 215 | /* systopia contrib end */ 216 | --------------------------------------------------------------------------------