├── .gitignore ├── LICENSE.txt ├── README.md ├── javadoc.xml ├── offline ├── java │ └── package-list ├── jdi │ └── package-list └── soot │ └── package-list ├── pom.xml └── src ├── main └── java │ └── vasco │ ├── BackwardInterProceduralAnalysis.java │ ├── CallSite.java │ ├── Context.java │ ├── ContextTransitionTable.java │ ├── DataFlowSolution.java │ ├── ForwardInterProceduralAnalysis.java │ ├── InterProceduralAnalysis.java │ ├── OldForwardInterProceduralAnalysis.java │ ├── ProgramRepresentation.java │ ├── callgraph │ ├── CallGraphTransformer.java │ ├── PointsToAnalysis.java │ └── PointsToGraph.java │ └── soot │ ├── ContextSensitiveJimpleRepresentation.java │ ├── DefaultJimpleRepresentation.java │ └── examples │ ├── CopyConstantAnalysis.java │ └── SignAnalysis.java └── test └── java └── vasco ├── callgraph └── CallGraphTest.java ├── soot └── examples │ ├── CopyConstantTest.java │ └── SignTest.java └── tests ├── CallGraphTestCase.java ├── CopyConstantTestCase.java └── SignTestCase.java /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | doc 3 | sootOutput 4 | *.class 5 | *.jar 6 | *~ 7 | out 8 | /target/ 9 | /bin2/ 10 | \.settings/ 11 | \.classpath 12 | \.project 13 | .idea 14 | *.iml 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, 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 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | 504 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VASCO 2 | ===== 3 | 4 | VASCO is a framework for performing precise inter-procedural data flow analysis using VAlue Sensitive COntexts. 5 | 6 | The framework classes are present in the package `vasco` and are described in the paper: [Interprocedural Data Flow Analysis in Soot using Value Contexts](http://dl.acm.org/citation.cfm?doid=2487568.2487569). 7 | 8 | You can use these classes directly with any program analysis toolkit or intermediate representation, although they work best with [Soot](http://www.sable.mcgill.ca/soot) and it's *Jimple* representation. 9 | 10 | ## API Documentation ## 11 | 12 | There is a JavaDoc generated [API documentation](http://rohanpadhye.github.io/vasco/apidocs) available for the framework classes. To develop a custom data-flow analysis, you need to extend either of the [`ForwardInterProceduralAnalysis`](https://rohanpadhye.github.io/vasco/apidocs/vasco/ForwardInterProceduralAnalysis.html) or [`BackwardInterProceduralAnalysis`](https://rohanpadhye.github.io/vasco/apidocs/vasco/BackwardInterProceduralAnalysis.html) classes. 13 | 14 | ## Building with Maven ## 15 | 16 | **Note**: Soot may not work with Java 9+, due to the absence of rt.jar in the new JDK. Until Soot's Java 9 support is released into the main stream, VASCO is also restricted to Java 8. If your default Java version is newer than Java 8, then set the `JAVA_HOME` environment variable to a Java 8 installation before continuing. 17 | 18 | ### Install locally ### 19 | 20 | Run `mvn install` in the VASCO directory after cloning the repository. You should now be able to add the following Maven dependency in any other Maven project on the same machine: 21 | 22 | ``` 23 | 24 | in.ac.iitb.cse 25 | vasco 26 | 1.1-SNAPSHOT 27 | 28 | ``` 29 | 30 | ### Standalone JAR ### 31 | 32 | Run `mvn package` in the VASCO directory after cloning the repository. 33 | 34 | This compiles the classes into the `target/classes` directory, along with a packaged JAR: `target/vasco-$VERSION.jar`. 35 | 36 | We are currently working on hosting the VASCO artifact to a repository for easy inclusion in other projects as a Maven dependency. 37 | 38 | ### Developing with Eclipse or IntelliJ IDEA ### 39 | 40 | Simply import as a Maven project. Everything should work out of the box. If not, please open an issue. 41 | 42 | ## Simple Examples ## 43 | 44 | The package [`vasco.soot.examples`](https://github.com/rohanpadhye/vasco/tree/master/src/main/java/vasco/soot/examples) contains some example analyses implemented for Soot such as **copy constant propagation** and a simple **sign analysis** (the latter is the same as the example used in the research paper). For each of these analysis, there is also a corresponding [driver class](https://github.com/rohanpadhye/vasco/tree/master/src/test/java/vasco/soot/examples) to run the analysis on some application with a main class. Try running the analyses on the [provided test cases](https://github.com/rohanpadhye/vasco/tree/master/src/test/java/vasco/tests) or any other Java program. 45 | 46 | You can run the examples on the command-line using the Maven exec plugin: 47 | 48 | ``` 49 | mvn exec:java -Dexec.mainClass="vasco.soot.examples.SignTest" -Dexec.args="-cp target/test-classes/ vasco.tests.SignTestCase" 50 | ``` 51 | 52 | ``` 53 | mvn exec:java -Dexec.mainClass="vasco.soot.examples.CopyConstantTest" -Dexec.args="-cp target/test-classes/ vasco.tests.CopyConstantTestCase" 54 | ``` 55 | 56 | ## Points-to Analysis ## 57 | 58 | The package [`vasco.callgraph`](https://github.com/rohanpadhye/vasco/tree/master/src/main/java/vasco/callgraph) contains a sophisticated points-to analysis that builds context-sensitive call graphs on-the-fly, as detailed in the paper. 59 | 60 | ### Experiments ### 61 | 62 | To run the experiments described in the paper, ensure that Soot is in your class path and execute: 63 | 64 | ``` 65 | mvn exec:java -Dexec.mainClass="vasco.callgraph.CallGraphTest" -Dexec.args="[-cp CLASSPATH] [-out DIR] [-k DEPTH] MAIN_CLASS" 66 | ``` 67 | 68 | Where: 69 | 70 | - `CLASSPATH` is used to locate application classes of the program to analyze (default: VASCO's own classpath) 71 | - `DIR` is the output directory where results will be dumped (default: `.`) 72 | - `DEPTH` is the maximum depth of call chains to count (default: `10`) 73 | - `MAIN_CLASS` is the entry point to the program 74 | 75 | This will generate a bunch of CSV files in the output directory containing statistics of analyzed methods, contexts, and call chains. 76 | 77 | ### Using Generated Call Graphs ### 78 | 79 | To use the generated call graphs programatically in your own Soot-based analysis, make sure to add the `vasco.callgraph.CallGraphTransformer` to Soot's analysis `PackManager` (see code in [`CallGraphTest.main()`](https://github.com/rohanpadhye/vasco/blob/master/src/test/java/vasco/callgraph/CallGraphTest.java) for how to do this) which set's the call-graphs in the global `Scene` appropriately. 80 | 81 | Any Soot-based analysis that you add to the pipe-line after the `CallGraphTransformer` can make use of the generated call-graphs by calling `Scene.v().getCallGraph()` or `Scene.v().getContextSensitiveCallGraph()` as you would with the results of SPARK and PADDLE respectively. 82 | 83 | 84 | ## Limitations when using with Java/Soot/Jimple ## 85 | 86 | The VASCO framework only implements the value-context-based inter-procedural analysis algorithm, and is not customized for Java in particular. This leads to several open issues that must be addressed by any application that uses VASCO for analyzing Java programs by integrating with Soot. 87 | 88 | - Native methods are `null` in the `DefaultJimpleRepresentation`. The handling of native methods probably depends on the client analysis, and thus you may have to write your own program representation with appropriate method stubs built in to the control-flow graph. 89 | - The JRE is not modelled. For example, `System.in` and `System.out` have initial values of `null` in the source code of `java.lang.System`. The JVM injects values for standard input/output streams at runtime. Other such JVM behaviours are not explicitly modeled. You may have to create a wrapper around the entry point to mock any JVM behavior that is relevant to your application. 90 | - Multi-threadeding is not handled within the framework itself. Some choose to solve this by simply linking calls to `Thread.start()` with the approriate `Runnable.run()` method, though the resulting analysis may not be sound if it does not consider interleavings of program execution. You can still use VASCO with multi-threaded programs if your analysis/application can make-do with making worst-case assumptions about shared data. 91 | - Reflection and/or dynamic class loading is not modeled by the framework. If your candidate program has this, you probably need to use some tool such as [TamiFlex](https://github.com/secure-software-engineering/tamiflex) to resolve dynamic bindings and transform your code to use static bindings before creating a `ProgramRepresentation`. 92 | 93 | ## Contributing ## 94 | 95 | If something seems fishy, please open an issue. If you know how to fix it, please submit a pull request. 96 | 97 | ### List of Contributors ### 98 | 99 | VASCO has mainly been developed by **Rohan Padhye** as part of his Master's thesis at [IIT Bombay](https://www.cse.iitb.ac.in), being advised by **Prof. Uday Khedker**. 100 | 101 | Over the years, VASCO's API and internals have been refined with the feedback from several contributors, including: 102 | - Eric Bodden 103 | - Johannes Späth 104 | - Vini Kanwar 105 | - Sushmita Nikose 106 | - Mandar Shinde 107 | - Linghui Luo 108 | 109 | -------------------------------------------------------------------------------- /javadoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /offline/java/package-list: -------------------------------------------------------------------------------- 1 | java.applet 2 | java.awt 3 | java.awt.color 4 | java.awt.datatransfer 5 | java.awt.dnd 6 | java.awt.event 7 | java.awt.font 8 | java.awt.geom 9 | java.awt.im 10 | java.awt.im.spi 11 | java.awt.image 12 | java.awt.image.renderable 13 | java.awt.print 14 | java.beans 15 | java.beans.beancontext 16 | java.io 17 | java.lang 18 | java.lang.annotation 19 | java.lang.instrument 20 | java.lang.management 21 | java.lang.ref 22 | java.lang.reflect 23 | java.math 24 | java.net 25 | java.nio 26 | java.nio.channels 27 | java.nio.channels.spi 28 | java.nio.charset 29 | java.nio.charset.spi 30 | java.rmi 31 | java.rmi.activation 32 | java.rmi.dgc 33 | java.rmi.registry 34 | java.rmi.server 35 | java.security 36 | java.security.acl 37 | java.security.cert 38 | java.security.interfaces 39 | java.security.spec 40 | java.sql 41 | java.text 42 | java.text.spi 43 | java.util 44 | java.util.concurrent 45 | java.util.concurrent.atomic 46 | java.util.concurrent.locks 47 | java.util.jar 48 | java.util.logging 49 | java.util.prefs 50 | java.util.regex 51 | java.util.spi 52 | java.util.zip 53 | javax.accessibility 54 | javax.activation 55 | javax.activity 56 | javax.annotation 57 | javax.annotation.processing 58 | javax.crypto 59 | javax.crypto.interfaces 60 | javax.crypto.spec 61 | javax.imageio 62 | javax.imageio.event 63 | javax.imageio.metadata 64 | javax.imageio.plugins.bmp 65 | javax.imageio.plugins.jpeg 66 | javax.imageio.spi 67 | javax.imageio.stream 68 | javax.jws 69 | javax.jws.soap 70 | javax.lang.model 71 | javax.lang.model.element 72 | javax.lang.model.type 73 | javax.lang.model.util 74 | javax.management 75 | javax.management.loading 76 | javax.management.modelmbean 77 | javax.management.monitor 78 | javax.management.openmbean 79 | javax.management.relation 80 | javax.management.remote 81 | javax.management.remote.rmi 82 | javax.management.timer 83 | javax.naming 84 | javax.naming.directory 85 | javax.naming.event 86 | javax.naming.ldap 87 | javax.naming.spi 88 | javax.net 89 | javax.net.ssl 90 | javax.print 91 | javax.print.attribute 92 | javax.print.attribute.standard 93 | javax.print.event 94 | javax.rmi 95 | javax.rmi.CORBA 96 | javax.rmi.ssl 97 | javax.script 98 | javax.security.auth 99 | javax.security.auth.callback 100 | javax.security.auth.kerberos 101 | javax.security.auth.login 102 | javax.security.auth.spi 103 | javax.security.auth.x500 104 | javax.security.cert 105 | javax.security.sasl 106 | javax.sound.midi 107 | javax.sound.midi.spi 108 | javax.sound.sampled 109 | javax.sound.sampled.spi 110 | javax.sql 111 | javax.sql.rowset 112 | javax.sql.rowset.serial 113 | javax.sql.rowset.spi 114 | javax.swing 115 | javax.swing.border 116 | javax.swing.colorchooser 117 | javax.swing.event 118 | javax.swing.filechooser 119 | javax.swing.plaf 120 | javax.swing.plaf.basic 121 | javax.swing.plaf.metal 122 | javax.swing.plaf.multi 123 | javax.swing.plaf.synth 124 | javax.swing.table 125 | javax.swing.text 126 | javax.swing.text.html 127 | javax.swing.text.html.parser 128 | javax.swing.text.rtf 129 | javax.swing.tree 130 | javax.swing.undo 131 | javax.tools 132 | javax.transaction 133 | javax.transaction.xa 134 | javax.xml 135 | javax.xml.bind 136 | javax.xml.bind.annotation 137 | javax.xml.bind.annotation.adapters 138 | javax.xml.bind.attachment 139 | javax.xml.bind.helpers 140 | javax.xml.bind.util 141 | javax.xml.crypto 142 | javax.xml.crypto.dom 143 | javax.xml.crypto.dsig 144 | javax.xml.crypto.dsig.dom 145 | javax.xml.crypto.dsig.keyinfo 146 | javax.xml.crypto.dsig.spec 147 | javax.xml.datatype 148 | javax.xml.namespace 149 | javax.xml.parsers 150 | javax.xml.soap 151 | javax.xml.stream 152 | javax.xml.stream.events 153 | javax.xml.stream.util 154 | javax.xml.transform 155 | javax.xml.transform.dom 156 | javax.xml.transform.sax 157 | javax.xml.transform.stax 158 | javax.xml.transform.stream 159 | javax.xml.validation 160 | javax.xml.ws 161 | javax.xml.ws.handler 162 | javax.xml.ws.handler.soap 163 | javax.xml.ws.http 164 | javax.xml.ws.soap 165 | javax.xml.ws.spi 166 | javax.xml.ws.wsaddressing 167 | javax.xml.xpath 168 | org.ietf.jgss 169 | org.omg.CORBA 170 | org.omg.CORBA.DynAnyPackage 171 | org.omg.CORBA.ORBPackage 172 | org.omg.CORBA.TypeCodePackage 173 | org.omg.CORBA.portable 174 | org.omg.CORBA_2_3 175 | org.omg.CORBA_2_3.portable 176 | org.omg.CosNaming 177 | org.omg.CosNaming.NamingContextExtPackage 178 | org.omg.CosNaming.NamingContextPackage 179 | org.omg.Dynamic 180 | org.omg.DynamicAny 181 | org.omg.DynamicAny.DynAnyFactoryPackage 182 | org.omg.DynamicAny.DynAnyPackage 183 | org.omg.IOP 184 | org.omg.IOP.CodecFactoryPackage 185 | org.omg.IOP.CodecPackage 186 | org.omg.Messaging 187 | org.omg.PortableInterceptor 188 | org.omg.PortableInterceptor.ORBInitInfoPackage 189 | org.omg.PortableServer 190 | org.omg.PortableServer.CurrentPackage 191 | org.omg.PortableServer.POAManagerPackage 192 | org.omg.PortableServer.POAPackage 193 | org.omg.PortableServer.ServantLocatorPackage 194 | org.omg.PortableServer.portable 195 | org.omg.SendingContext 196 | org.omg.stub.java.rmi 197 | org.w3c.dom 198 | org.w3c.dom.bootstrap 199 | org.w3c.dom.events 200 | org.w3c.dom.ls 201 | org.xml.sax 202 | org.xml.sax.ext 203 | org.xml.sax.helpers 204 | -------------------------------------------------------------------------------- /offline/jdi/package-list: -------------------------------------------------------------------------------- 1 | com.sun.jdi 2 | com.sun.jdi.connect 3 | com.sun.jdi.connect.spi 4 | com.sun.jdi.event 5 | com.sun.jdi.request 6 | -------------------------------------------------------------------------------- /offline/soot/package-list: -------------------------------------------------------------------------------- 1 | soot 2 | soot.baf 3 | soot.baf.internal 4 | soot.baf.toolkits.base 5 | soot.coffi 6 | soot.dava 7 | soot.dava.internal.AST 8 | soot.dava.internal.SET 9 | soot.dava.internal.asg 10 | soot.dava.internal.javaRep 11 | soot.dava.toolkits.base.AST 12 | soot.dava.toolkits.base.AST.analysis 13 | soot.dava.toolkits.base.AST.interProcedural 14 | soot.dava.toolkits.base.AST.structuredAnalysis 15 | soot.dava.toolkits.base.AST.transformations 16 | soot.dava.toolkits.base.AST.traversals 17 | soot.dava.toolkits.base.DavaMonitor 18 | soot.dava.toolkits.base.finders 19 | soot.dava.toolkits.base.misc 20 | soot.dava.toolkits.base.renamer 21 | soot.grimp 22 | soot.grimp.internal 23 | soot.grimp.toolkits.base 24 | soot.javaToJimple 25 | soot.javaToJimple.jj 26 | soot.javaToJimple.jj.ast 27 | soot.javaToJimple.jj.types 28 | soot.javaToJimple.toolkits 29 | soot.jbco 30 | soot.jbco.bafTransformations 31 | soot.jbco.gui 32 | soot.jbco.jimpleTransformations 33 | soot.jbco.util 34 | soot.jimple 35 | soot.jimple.internal 36 | soot.jimple.paddle 37 | soot.jimple.parser 38 | soot.jimple.parser.analysis 39 | soot.jimple.parser.lexer 40 | soot.jimple.parser.node 41 | soot.jimple.parser.parser 42 | soot.jimple.spark 43 | soot.jimple.spark.builder 44 | soot.jimple.spark.fieldrw 45 | soot.jimple.spark.geom 46 | soot.jimple.spark.geom.geomE 47 | soot.jimple.spark.geom.geomPA 48 | soot.jimple.spark.geom.heapinsE 49 | soot.jimple.spark.geom.ptinsE 50 | soot.jimple.spark.internal 51 | soot.jimple.spark.ondemand 52 | soot.jimple.spark.ondemand.genericutil 53 | soot.jimple.spark.ondemand.pautil 54 | soot.jimple.spark.pag 55 | soot.jimple.spark.sets 56 | soot.jimple.spark.solver 57 | soot.jimple.toolkits.annotation 58 | soot.jimple.toolkits.annotation.arraycheck 59 | soot.jimple.toolkits.annotation.callgraph 60 | soot.jimple.toolkits.annotation.defs 61 | soot.jimple.toolkits.annotation.fields 62 | soot.jimple.toolkits.annotation.j5anno 63 | soot.jimple.toolkits.annotation.liveness 64 | soot.jimple.toolkits.annotation.logic 65 | soot.jimple.toolkits.annotation.methods 66 | soot.jimple.toolkits.annotation.nullcheck 67 | soot.jimple.toolkits.annotation.parity 68 | soot.jimple.toolkits.annotation.profiling 69 | soot.jimple.toolkits.annotation.purity 70 | soot.jimple.toolkits.annotation.qualifiers 71 | soot.jimple.toolkits.annotation.tags 72 | soot.jimple.toolkits.base 73 | soot.jimple.toolkits.callgraph 74 | soot.jimple.toolkits.graph 75 | soot.jimple.toolkits.infoflow 76 | soot.jimple.toolkits.invoke 77 | soot.jimple.toolkits.pointer 78 | soot.jimple.toolkits.pointer.nativemethods 79 | soot.jimple.toolkits.pointer.representations 80 | soot.jimple.toolkits.pointer.util 81 | soot.jimple.toolkits.reflection 82 | soot.jimple.toolkits.scalar 83 | soot.jimple.toolkits.scalar.pre 84 | soot.jimple.toolkits.thread 85 | soot.jimple.toolkits.thread.mhp 86 | soot.jimple.toolkits.thread.mhp.findobject 87 | soot.jimple.toolkits.thread.mhp.pegcallgraph 88 | soot.jimple.toolkits.thread.mhp.stmt 89 | soot.jimple.toolkits.thread.synchronization 90 | soot.jimple.toolkits.typing 91 | soot.jimple.toolkits.typing.fast 92 | soot.jimple.toolkits.typing.integer 93 | soot.options 94 | soot.rtlib.tamiflex 95 | soot.shimple 96 | soot.shimple.internal 97 | soot.shimple.toolkits.graph 98 | soot.shimple.toolkits.scalar 99 | soot.sootify 100 | soot.tagkit 101 | soot.toolkits.astmetrics 102 | soot.toolkits.astmetrics.DataHandlingApplication 103 | soot.toolkits.exceptions 104 | soot.toolkits.graph 105 | soot.toolkits.graph.interaction 106 | soot.toolkits.graph.pdg 107 | soot.toolkits.scalar 108 | soot.tools 109 | soot.util 110 | soot.util.cfgcmd 111 | soot.util.dot 112 | soot.util.queue 113 | soot.xml 114 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | in.ac.iitb.cse 5 | vasco 6 | 1.1-SNAPSHOT 7 | 8 | 1.7 9 | 1.7 10 | UTF-8 11 | 12 | 13 | 14 | 15 | maven-compiler-plugin 16 | 3.8.1 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.codehaus.mojo 24 | exec-maven-plugin 25 | 3.0.0 26 | 27 | 28 | 29 | exec 30 | 31 | 32 | 33 | 34 | test 35 | /tmp 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-surefire-plugin 41 | 2.22.2 42 | 43 | 1 44 | false 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.soot-oss 52 | soot 53 | 4.2.1 54 | 55 | 56 | junit 57 | junit 58 | 4.13.1 59 | test 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/vasco/BackwardInterProceduralAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.LinkedList; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | /** 25 | * A generic backward-flow inter-procedural analysis which is fully 26 | * context-sensitive. 27 | * 28 | *

29 | * This class essentially captures a backward data flow problem which can be 30 | * solved using the context-sensitive inter-procedural analysis framework as 31 | * described in {@link InterProceduralAnalysis}. 32 | *

33 | * 34 | *

35 | * This is the class that client analyses will extend in order to perform 36 | * backward-flow inter-procedural analysis. 37 | *

38 | * 39 | * @author Rohan Padhye 40 | * 41 | * @param the type of a method 42 | * @param the type of a node in the CFG 43 | * @param the type of a data flow value 44 | */ 45 | public abstract class BackwardInterProceduralAnalysis extends InterProceduralAnalysis { 46 | 47 | /** Constructs a new backward-flow inter-procedural analysis. */ 48 | public BackwardInterProceduralAnalysis() { 49 | // Kick-up to the super with the BACKWARD direction. 50 | super(true); 51 | 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | @Override 58 | public void doAnalysis() { 59 | 60 | // Initial contexts 61 | for (M method : programRepresentation().getEntryPoints()) { 62 | initContext(method, boundaryValue(method)); 63 | } 64 | 65 | // Perform work-list based analysis 66 | while (!worklist.isEmpty()) { 67 | // Get the newest context on the work-list 68 | Context currentContext = worklist.last(); 69 | 70 | // If this context has no more nodes to analyze, then take it out of the work-list 71 | if (currentContext.getWorkList().isEmpty()) { 72 | worklist.remove(currentContext); 73 | continue; 74 | } 75 | 76 | 77 | // Remove the next node to process from the context's work-list 78 | N node = currentContext.getWorkList().pollFirst(); 79 | 80 | if (node != null) { 81 | // Compute the OUT data flow value (only for non-exit units). 82 | List successors = currentContext.getControlFlowGraph().getSuccsOf(node); 83 | if (successors.size() != 0) { 84 | // Initialise to the TOP value 85 | A out = topValue(); 86 | // Merge IN values of all successors 87 | for (N succ : successors) { 88 | A succIn = currentContext.getValueBefore(succ); 89 | out = meet(out, succIn); 90 | } 91 | // Set the OUT value at the node to the result 92 | currentContext.setValueAfter(node, out); 93 | } 94 | 95 | // Store the value of IN before the flow function is processed. 96 | A prevIn = currentContext.getValueBefore(node); 97 | 98 | // Get the value of OUT 99 | A out = currentContext.getValueAfter(node); 100 | 101 | 102 | //System.out.println("OUT(" + node + ") = " + out); 103 | 104 | // Now to compute the IN value 105 | A in; 106 | 107 | // Handle flow functions depending on whether this is a call statement or not 108 | if (programRepresentation().isCall(node)) { 109 | 110 | in = topValue(); 111 | 112 | boolean hit = false; 113 | 114 | for (M targetMethod : programRepresentation().resolveTargets(currentContext.getMethod(), node)) { 115 | A exitValue = callExitFlowFunction(currentContext, targetMethod, node, out); 116 | 117 | CallSite callSite = new CallSite(currentContext, node); 118 | 119 | // Check if the called method has a context associated with this exit flow: 120 | Context targetContext = getContext(targetMethod, exitValue); 121 | // If not, then set 'targetContext' to a new context with the given exit flow. 122 | if (targetContext == null) { 123 | targetContext = initContext(targetMethod, exitValue); 124 | if (verbose) { 125 | System.out.println("[NEW] X" + currentContext + " -> X" + targetContext + " " + targetMethod + " "); 126 | } 127 | //System.out.println("EXIT(X"+targetContext+") = " + exitValue); 128 | } 129 | 130 | // Store the transition from the calling context and site to the called context. 131 | contextTransitions.addTransition(callSite, targetContext); 132 | 133 | // Check if the target context has been analysed (surely not if it is just newly made): 134 | if (targetContext.isAnalysed()) { 135 | hit = true; 136 | if (verbose) { 137 | System.out.println("[HIT] X" + currentContext + " -> X" + targetContext + " " + targetMethod + " "); 138 | } 139 | A entryValue = targetContext.getEntryValue(); 140 | //System.out.println("ENTRY(X"+targetContext+") = " + entryValue); 141 | 142 | A callValue = callEntryFlowFunction(currentContext, targetMethod, node, entryValue); 143 | 144 | in = meet(in, callValue); 145 | 146 | } 147 | } 148 | 149 | // If there was at least one hit, continue propagation 150 | if (hit) { 151 | A localValue = callLocalFlowFunction(currentContext, node, out); 152 | in = meet(in, localValue); 153 | } 154 | } else { 155 | in = normalFlowFunction(currentContext, node, out); 156 | } 157 | 158 | //System.out.println("IN(" + node + ") = " + in); 159 | //System.out.println("---------------------------------------"); 160 | 161 | // Merge with previous IN to force monotonicity (harmless if flow functions are monotonic) 162 | in = meet(in, prevIn); 163 | 164 | // Set the IN value 165 | currentContext.setValueBefore(node, in); 166 | 167 | // If IN has changed... 168 | if (in.equals(prevIn) == false) { 169 | // Then add predecessors to the work-list. 170 | for (N predecessors : currentContext.getControlFlowGraph().getPredsOf(node)) { 171 | currentContext.getWorkList().add(predecessors); 172 | } 173 | } 174 | // If the unit is in HEADS, then we have at least one 175 | // path to the start of the method, so add the NULL unit 176 | if (currentContext.getControlFlowGraph().getHeads().contains(node)) { 177 | currentContext.getWorkList().add(null); 178 | } 179 | } else { 180 | // NULL unit, which means the end of the method. 181 | assert (currentContext.getWorkList().isEmpty()); 182 | 183 | // Entry value is the merge of the INs of the head nodes. 184 | A entryValue = topValue(); 185 | for (N headNode : currentContext.getControlFlowGraph().getHeads()) { 186 | A headIn = currentContext.getValueBefore(headNode); 187 | entryValue = meet(entryValue, headIn); 188 | } 189 | 190 | // Set the entry value of the context. 191 | currentContext.setEntryValue(entryValue); 192 | 193 | // Mark this context as analysed at least once. 194 | currentContext.markAnalysed(); 195 | 196 | // Add callers to work-list, if any 197 | Set> callers = contextTransitions.getCallers(currentContext); 198 | if (callers != null) { 199 | for (CallSite callSite : callers) { 200 | // Extract the calling context and node from the caller site. 201 | Context callingContext = callSite.getCallingContext(); 202 | N callNode = callSite.getCallNode(); 203 | // Add the calling unit to the calling context's node work-list. 204 | callingContext.getWorkList().add(callNode); 205 | // Ensure that the calling context is on the context work-list. 206 | worklist.add(callingContext); 207 | } 208 | } 209 | 210 | // Free memory on-the-fly if not needed 211 | if (freeResultsOnTheFly) { 212 | Set> reachableContexts = contextTransitions.reachableSet(currentContext, true); 213 | // If any reachable contexts exist on the work-list, then we cannot free memory 214 | boolean canFree = true; 215 | for (Context reachableContext : reachableContexts) { 216 | if (worklist.contains(reachableContext)) { 217 | canFree = false; 218 | break; 219 | } 220 | } 221 | // If no reachable contexts on the stack, then free memory associated 222 | // with this context 223 | if (canFree) { 224 | for (Context reachableContext : reachableContexts) { 225 | reachableContext.freeMemory(); 226 | } 227 | } 228 | } 229 | } 230 | 231 | } 232 | 233 | // Sanity check 234 | for (List> contextList : contexts.values()) { 235 | for (Context context : contextList) { 236 | if (context.isAnalysed() == false) { 237 | System.err.println("*** ATTENTION ***: Only partial analysis of X" + context + 238 | " " + context.getMethod()); 239 | } 240 | } 241 | } 242 | } 243 | 244 | /** 245 | * Creates a new value context and initialises data flow values for its nodes. 246 | * 247 | *

248 | * The following steps are performed: 249 | *

    250 | *
  1. Construct the context.
  2. 251 | *
  3. Initialise IN/OUT for all nodes and add them to the work-list
  4. 252 | *
  5. Initialise the OUT of exit points with a copy of the given exit value.
  6. 253 | *
  7. Add this new context to the given method's mapping.
  8. 254 | *
  9. Add this context to the global work-list.
  10. 255 | *
256 | *

257 | * 258 | * @param method the method whose context to create 259 | * @param exitValue the data flow value at the exit of this method 260 | */ 261 | protected Context initContext(M method, A exitValue) { 262 | // Construct the context 263 | Context context = new Context(method, programRepresentation().getControlFlowGraph(method), true); 264 | 265 | // Initialise IN/OUT for all nodes and add them to the work-list 266 | for (N unit : context.getControlFlowGraph()) { 267 | context.setValueBefore(unit, topValue()); 268 | context.setValueAfter(unit, topValue()); 269 | context.getWorkList().add(unit); 270 | } 271 | 272 | // Now, initialise the OUT of exit points with a copy of the given exit value. 273 | context.setExitValue(copy(exitValue)); 274 | for (N unit : context.getControlFlowGraph().getTails()) { 275 | context.setValueAfter(unit, copy(exitValue)); 276 | } 277 | context.setEntryValue(topValue()); 278 | 279 | // Add this new context to the given method's mapping. 280 | if (!contexts.containsKey(method)) { 281 | contexts.put(method, new LinkedList>()); 282 | } 283 | contexts.get(method).add(context); 284 | 285 | // Add this context to the global work-list 286 | worklist.add(context); 287 | 288 | return context; 289 | 290 | } 291 | 292 | /** 293 | * Processes the intra-procedural flow function of a statement that does 294 | * not contain a method call. 295 | * 296 | * @param context the value context at the call-site 297 | * @param node the statement whose flow function to process 298 | * @param outValue the data flow value after the statement 299 | * @return the data flow value before the statement 300 | */ 301 | public abstract A normalFlowFunction(Context context, N node, A outValue); 302 | 303 | /** 304 | * Processes the inter-procedural flow function for a method call at 305 | * the start of the call, to handle parameters. 306 | * 307 | * @param context the value context at the call-site 308 | * @param targetMethod the target (or one of the targets) of this call site 309 | * @param node the statement containing the method call 310 | * @param entryValue the data flow value at the entry to the called procedure 311 | * @return the data flow value before the call (call component) 312 | */ 313 | public abstract A callEntryFlowFunction(Context context, M targetMethod, N node, A entryValue); 314 | 315 | /** 316 | * Processes the inter-procedural flow function for a method call at the 317 | * end of the call, to handle return values. 318 | * 319 | * @param context the value context at the call-site 320 | * @param targetMethod the target (or one of the targets) of this call site 321 | * @param node the statement containing the method call 322 | * @param outValue the data flow value after the call 323 | * @return the data flow value at the exit of the called procedure 324 | */ 325 | public abstract A callExitFlowFunction(Context context, M targetMethod, N node, A outValue); 326 | 327 | /** 328 | * 329 | * Processes the intra-procedural flow function for a method call at the 330 | * call-site itself, to handle propagation of local values that are not 331 | * involved in the call. 332 | * 333 | * @param context the value context at the call-site 334 | * @param node the statement containing the method call 335 | * @param outValue the data flow value after the call 336 | * @return the data flow value before the call (local component) 337 | */ 338 | public abstract A callLocalFlowFunction(Context context, N node, A outValue); 339 | 340 | 341 | 342 | 343 | } 344 | -------------------------------------------------------------------------------- /src/main/java/vasco/CallSite.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | /** 21 | * A context-sensitive location of a method invocation. 22 | * 23 | *

24 | * A call-site is a uniquely identified by the calling context and the 25 | * node containing the call statement. 26 | *

27 | * 28 | * @author Rohan Padhye 29 | * 30 | * @param the type of a method 31 | * @param the type of a node in the CFG 32 | * @param
the type of a data flow value 33 | */ 34 | public class CallSite implements Comparable> { 35 | 36 | /** The context at the caller. */ 37 | private final Context callingContext; 38 | 39 | /** The node at which the call is made. */ 40 | private final N callNode; 41 | 42 | /** Constructs a new call site with the given parameters. */ 43 | public CallSite(Context callingContext, N callStmt) { 44 | this.callingContext = callingContext; 45 | this.callNode = callStmt; 46 | } 47 | 48 | /** 49 | * Call-sites are ordered by the ordering of their context's IDs. 50 | * 51 | * This functionality is useful in the framework's internal methods 52 | * where ordered processing of newer contexts first helps speed up 53 | * certain operations. 54 | */ 55 | @Override 56 | public int compareTo(CallSite other) { 57 | return this.callingContext.getId() - other.callingContext.getId(); 58 | } 59 | 60 | /* 61 | * (non-Javadoc) 62 | * 63 | * @see java.lang.Object#equals(java.lang.Object) 64 | */ 65 | @SuppressWarnings("rawtypes") 66 | @Override 67 | public boolean equals(Object obj) { 68 | if (this == obj) 69 | return true; 70 | if (obj == null) 71 | return false; 72 | if (getClass() != obj.getClass()) 73 | return false; 74 | CallSite other = (CallSite) obj; 75 | if (callNode == null) { 76 | if (other.callNode != null) 77 | return false; 78 | } else if (!callNode.equals(other.callNode)) 79 | return false; 80 | if (callingContext == null) { 81 | if (other.callingContext != null) 82 | return false; 83 | } else if (!callingContext.equals(other.callingContext)) 84 | return false; 85 | return true; 86 | } 87 | 88 | /** 89 | * Returns the value context at this call-site. 90 | * 91 | * @return the value context at this call-site 92 | */ 93 | public Context getCallingContext() { 94 | return callingContext; 95 | } 96 | 97 | /** 98 | * Returns the calling node. 99 | * 100 | * @return the calling node 101 | */ 102 | public N getCallNode() { 103 | return callNode; 104 | } 105 | 106 | /* 107 | * (non-Javadoc) 108 | * 109 | * @see java.lang.Object#hashCode() 110 | */ 111 | @Override 112 | public int hashCode() { 113 | final int prime = 31; 114 | int result = 1; 115 | result = prime * result + ((callNode == null) ? 0 : callNode.hashCode()); 116 | result = prime * result + ((callingContext == null) ? 0 : callingContext.hashCode()); 117 | return result; 118 | } 119 | 120 | /** 121 | * Returns a string representation of this call-site. 122 | * @return a string representation of this call-site 123 | */ 124 | @Override 125 | public String toString() { 126 | return "X"+callingContext+": "+callNode; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/vasco/Context.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.Comparator; 21 | import java.util.HashMap; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.NavigableSet; 26 | import java.util.TreeSet; 27 | 28 | import com.google.common.collect.HashBasedTable; 29 | import com.google.common.collect.Table; 30 | 31 | import soot.toolkits.graph.DirectedGraph; 32 | import soot.toolkits.graph.SlowPseudoTopologicalOrderer; 33 | import soot.toolkits.scalar.Pair; 34 | 35 | /** 36 | * A value-based context for a context-sensitive inter-procedural data flow 37 | * analysis. 38 | * 39 | *

40 | * A value-based context is identified as a pair of a method and the data 41 | * flow value at the entry of the method, for forward flows, or the data 42 | * flow value at the exit of the method, for backward flows. Thus, if 43 | * two distinct calls are made to a method and each call-site has the same 44 | * data flow value then it is considered that the target of that call is 45 | * the same context. This concept allows termination in the presence of 46 | * recursion as the number of contexts is limited by the size of the lattice 47 | * (which must be finite). 48 | *

49 | * 50 | *

51 | * Each value context has its own work-list of CFG nodes to analyse, and the 52 | * results of analysis are stored in a map from nodes to the data flow values 53 | * before/after the node. 54 | *

55 | * 56 | * @author Rohan Padhye 57 | * 58 | * @param the type of a method 59 | * @param the type of a node in the CFG 60 | * @param
the type of a data flow value 61 | */ 62 | public class Context implements soot.Context, Comparable> { 63 | 64 | /** A counter for global context identifiers. */ 65 | private static int count = 0; 66 | 67 | /** Debug stuff */ 68 | static java.util.Set freeContexts = new java.util.HashSet(); 69 | static int totalNodes = 0; 70 | static int liveNodes = 0; 71 | 72 | /** Whether or not this context has been fully analysed at least once. */ 73 | private boolean analysed; 74 | 75 | /** The control-flow graph of this method's body. */ 76 | private DirectedGraph controlFlowGraph; 77 | 78 | /** The data flow value associated with the entry to the method. **/ 79 | private A entryValue; 80 | 81 | /** The data flow value associated with the exit of the method. */ 82 | private A exitValue; 83 | 84 | /** A globally unique identifier. */ 85 | private int id; 86 | 87 | 88 | /** The method for which this calling context context applies. */ 89 | private M method; 90 | 91 | /** The data flow values at the exit of each node. */ 92 | private Map outValues; 93 | 94 | /** The data flow values at the entry of each node. */ 95 | private Map inValues; 96 | 97 | private Table vals = HashBasedTable.create(); 98 | 99 | /** The work-list of nodes that still need to be analysed. */ 100 | private NavigableSet workList; 101 | 102 | private LinkedList> workListOfEdges; 103 | /** 104 | * Creates a new context for phantom method 105 | * 106 | * @param method 107 | */ 108 | public Context(M method) { 109 | count++; 110 | this.id = count; 111 | this.method = method; 112 | this.inValues = new HashMap(); 113 | this.outValues = new HashMap(); 114 | this.analysed = false; 115 | this.workList = new TreeSet(); 116 | this.workListOfEdges = new LinkedList>(); 117 | } 118 | 119 | /** 120 | * Creates a new context for the given method. 121 | * 122 | * @param method 123 | * the method to which this value context belongs 124 | * @param cfg 125 | * the control-flow graph for the body of method 126 | * @param reverse 127 | * true if the analysis is in the reverse direction, and 128 | * false if the analysis is in the forward direction 129 | */ 130 | public Context(M method, DirectedGraph cfg, boolean reverse) { 131 | // Increment count and set id. 132 | count++; 133 | this.id = count; 134 | 135 | // Initialise fields. 136 | this.method = method; 137 | this.controlFlowGraph = cfg; 138 | this.inValues = new HashMap(); 139 | this.outValues = new HashMap(); 140 | this.analysed = false; 141 | 142 | totalNodes = totalNodes + controlFlowGraph.size(); 143 | liveNodes = liveNodes + controlFlowGraph.size(); 144 | 145 | // Now to initialise work-list. First, let's create a total order. 146 | @SuppressWarnings("unchecked") 147 | List orderedNodes = new SlowPseudoTopologicalOrderer().newList(controlFlowGraph, reverse); 148 | // Then a mapping from a N to the position in the order. 149 | final Map numbers = new HashMap(); 150 | int num = 1; 151 | for (N N : orderedNodes) { 152 | numbers.put(N, num); 153 | num++; 154 | } 155 | // Map the lowest priority to the null N, which is used to aggregate 156 | // ENTRY/EXIT flows. 157 | numbers.put(null, Integer.MAX_VALUE); 158 | // Now, create a sorted set with a comparator created on-the-fly using 159 | // the total order. 160 | this.workList = new TreeSet(new Comparator() { 161 | @Override 162 | public int compare(N u, N v) { 163 | return numbers.get(u) - numbers.get(v); 164 | } 165 | }); 166 | this.workListOfEdges = new LinkedList>(); 167 | } 168 | 169 | /** 170 | * Compares two contexts by their globally unique IDs. 171 | * 172 | * This functionality is useful in the framework's internal methods 173 | * where ordered processing of newer contexts first helps speed up 174 | * certain operations. 175 | */ 176 | @Override 177 | public int compareTo(Context other) { 178 | return this.getId() - other.getId(); 179 | } 180 | 181 | /** 182 | * Destroys all data flow information associated with the nodes 183 | * of this context. 184 | */ 185 | public void freeMemory() { 186 | liveNodes = liveNodes - controlFlowGraph.size(); 187 | inValues = null; 188 | outValues = null; 189 | controlFlowGraph = null; 190 | workList = null; 191 | freeContexts.add(this); 192 | } 193 | 194 | /** 195 | * Returns a reference to the control flow graph of this context's method. 196 | * 197 | * @return a reference to the control flow graph of this context's method 198 | */ 199 | public DirectedGraph getControlFlowGraph() { 200 | return controlFlowGraph; 201 | } 202 | 203 | /** Returns the total number of contexts created so far. */ 204 | public static int getCount() { 205 | return count; 206 | } 207 | 208 | /** 209 | * Returns a reference to the data flow value at the method entry. 210 | * 211 | * @return a reference to the data flow value at the method entry 212 | */ 213 | public A getEntryValue() { 214 | return entryValue; 215 | } 216 | 217 | /** 218 | * Returns a reference to the data flow value at the method exit. 219 | * 220 | * @return a reference to the data flow value at the method exit 221 | */ 222 | public A getExitValue() { 223 | return exitValue; 224 | } 225 | 226 | /** 227 | * Returns the globally unique identifier of this context. 228 | * 229 | * @return the globally unique identifier of this context 230 | */ 231 | public int getId() { 232 | return id; 233 | } 234 | 235 | /** 236 | * Returns a reference to this context's method. 237 | * 238 | * @return a reference to this context's method 239 | */ 240 | public M getMethod() { 241 | return method; 242 | } 243 | 244 | public A getEdgeValue(N node, N succ) { 245 | return this.vals.get(node, succ); 246 | } 247 | 248 | public void setEdgeValue(N node, N succ, A val) { 249 | if (this.vals == null) { 250 | this.vals=HashBasedTable.create(); 251 | } 252 | this.vals.put(node, succ, val); 253 | } 254 | /** 255 | * Gets the data flow value at the exit of the given node. 256 | * 257 | * @param node a node in the control flow graph 258 | * @return the data flow value at the exit of the given node 259 | */ 260 | public A getValueAfter(N node) { 261 | return outValues.get(node); 262 | } 263 | 264 | /** 265 | * Gets the data flow value at the entry of the given node. 266 | * 267 | * @param node a node in the control flow graph 268 | * @return the data flow value at the entry of the given node 269 | */ 270 | public A getValueBefore(N node) { 271 | return inValues.get(node); 272 | } 273 | 274 | /** 275 | * Returns a reference to this context's work-list. 276 | * 277 | * @return a reference to this context's work-list 278 | */ 279 | public NavigableSet getWorkList() { 280 | return workList; 281 | } 282 | 283 | public LinkedList> getWorkListOfEdges() { 284 | return this.workListOfEdges; 285 | } 286 | 287 | /** 288 | * Returns whether or not this context has been analysed at least once. 289 | * 290 | * @return true if the context has been analysed at least once, 291 | * or false otherwise 292 | */ 293 | public boolean isAnalysed() { 294 | return analysed; 295 | } 296 | 297 | /** 298 | * Returns whether the information about individual CFG nodes has 299 | * been released. 300 | * 301 | * @return true if the context data has been released 302 | */ 303 | boolean isFreed() { 304 | return inValues == null && outValues == null; 305 | } 306 | 307 | /** 308 | * Marks this context as analysed. 309 | */ 310 | public void markAnalysed() { 311 | this.analysed = true; 312 | } 313 | 314 | /** 315 | * Sets the entry flow of this context. 316 | * 317 | * @param entryValue the new data flow value at the method entry 318 | */ 319 | public void setEntryValue(A entryValue) { 320 | this.entryValue = entryValue; 321 | } 322 | 323 | /** 324 | * Sets the exit flow of this context. 325 | * 326 | * @param exitValue the new data flow value at the method exit 327 | */ 328 | public void setExitValue(A exitValue) { 329 | this.exitValue = exitValue; 330 | } 331 | /** 332 | * Sets the data flow value at the exit of the given node. 333 | * 334 | * @param node a node in the control flow graph 335 | * @param value the new data flow at the node exit 336 | */ 337 | public void setValueAfter(N node, A value) { 338 | outValues.put(node, value); 339 | } 340 | /** 341 | * Sets the data flow value at the entry of the given node. 342 | * 343 | * @param node a node in the control flow graph 344 | * @param value the new data flow at the node entry 345 | */ 346 | public void setValueBefore(N node, A value) { 347 | inValues.put(node, value); 348 | } 349 | 350 | /** {@inheritDoc} */ 351 | @Override 352 | public String toString() { 353 | return Integer.toString(id); 354 | } 355 | 356 | public void unmarkAnalysed() { 357 | this.analysed = false; 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/main/java/vasco/ContextTransitionTable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.HashSet; 23 | import java.util.Map; 24 | import java.util.Set; 25 | import java.util.Stack; 26 | 27 | /** 28 | * A record of transitions between contexts at call-sites. 29 | * 30 | *

The context transition table records a bidirectional one-to-many mapping 31 | * of call-sites to called contexts parameterised by their methods.

32 | * 33 | *

If a call-site transition is not traversed in an analysis (e.g. a call 34 | * to a native method) then it is listed as a "default site" which this table 35 | * also records. 36 | * 37 | * 38 | * @author Rohan Padhye 39 | * 40 | * @param the type of a method 41 | * @param the type of a node in the CFG 42 | * @param the type of a data flow value 43 | */ 44 | public class ContextTransitionTable { 45 | 46 | /** A map from contexts to a set of call-sites that transition to it. */ 47 | protected Map,Set>> callers; 48 | 49 | /** A map from call-sites to contexts, parameterised by the called method. */ 50 | protected Map,Map>> transitions; 51 | 52 | /** A map of contexts to call-sites present within their method bodies. */ 53 | protected Map,Set>> callSitesOfContexts; 54 | 55 | /** A set of call-sites from which transitions are unknown. */ 56 | protected Set> defaultCallSites; 57 | 58 | /** Constructs a new context transition table with no initial entries. */ 59 | public ContextTransitionTable() { 60 | transitions = new HashMap,Map>>(); 61 | callers = new HashMap,Set>>(); 62 | callSitesOfContexts = new HashMap,Set>>(); 63 | defaultCallSites = new HashSet>(); 64 | } 65 | 66 | /** 67 | * Adds a transition to the table. 68 | * 69 | *

If the target context is specified as null, the source 70 | * call-site is considered a "default site" from which transitions are 71 | * unknown. This is used to model unpredictable targets, for example, 72 | * when encountering calls to native methods.

73 | * 74 | *

Any previous transitions from the source call site to other 75 | * contexts of the same called method are deleted.

76 | * 77 | * @param callSite the call-site which is the source of the transition 78 | * @param targetContext the value context which is the target of the call-site 79 | */ 80 | public void addTransition(CallSite callSite, Context targetContext) { 81 | 82 | if (targetContext != null) { 83 | // Get the target method 84 | M targetMethod = targetContext.getMethod(); 85 | 86 | // Ensure memory allocated in the reverse direction 87 | if (!callers.containsKey(targetContext)) { 88 | callers.put(targetContext, new HashSet>()); 89 | } 90 | 91 | // Remove previous entry in the reverse direction 92 | if (transitions.containsKey(callSite) && transitions.get(callSite).containsKey(targetMethod)) { 93 | Context oldTarget = transitions.get(callSite).get(targetMethod); 94 | callers.get(oldTarget).remove(callSite); 95 | } 96 | 97 | // Ensure memory allocated in the forward direction 98 | if (!transitions.containsKey(callSite)) { 99 | transitions.put(callSite, new HashMap>()); 100 | } 101 | // Make entry in the forward direction 102 | transitions.get(callSite).put(targetMethod, targetContext); 103 | 104 | // Make entry in the reverse direction 105 | callers.get(targetContext).add(callSite); 106 | } else { 107 | // A null target means incomplete information (or "default") 108 | // Remove previous entries in the reverse direction 109 | if (transitions.containsKey(callSite) && transitions.get(callSite) != null) { 110 | for (M method : transitions.get(callSite).keySet()) { 111 | Context oldTarget = transitions.get(callSite).get(method); 112 | callers.get(oldTarget).remove(callSite); 113 | } 114 | } 115 | // Add to default call sites 116 | defaultCallSites.add(callSite); 117 | } 118 | 119 | // Ensure memory allocated for call-site index 120 | Context source = callSite.getCallingContext(); 121 | if (callSitesOfContexts.containsKey(source) == false) { 122 | callSitesOfContexts.put(source, new HashSet>()); 123 | } 124 | 125 | // Add call-site to source context 126 | callSitesOfContexts.get(source).add(callSite); 127 | } 128 | 129 | /** 130 | * Returns an unmodifiable view of the mapping from contexts to their callers. 131 | * @return an unmodifiable view of the mapping from contexts to their callers 132 | */ 133 | public Map,Set>> getCallers() { 134 | return Collections.unmodifiableMap(callers); 135 | } 136 | 137 | /** 138 | * Returns the callers of a value context. 139 | * 140 | * @param target the target value context 141 | * @return a set of call-sites which transition to the given target context 142 | */ 143 | public Set> getCallers(Context target) { 144 | return callers.get(target); 145 | } 146 | 147 | /** 148 | * Returns an unmodifiable view of a mapping from calling contexts to all their call-sites. 149 | * @return an unmodifiable view of a mapping from calling contexts to all their call-sites 150 | */ 151 | public Map,Set>> getCallSitesOfContexts() { 152 | return Collections.unmodifiableMap(callSitesOfContexts); 153 | } 154 | 155 | /** 156 | * Returns an unmodifiable view of the set of call-sites marked "default". 157 | * @return an unmodifiable view of the set of call-sites marked "default" 158 | */ 159 | public Set> getDefaultCallSites() { 160 | return defaultCallSites; 161 | } 162 | 163 | /** 164 | * Returns the targets of a call-site. 165 | * 166 | * @param callSite the source of the transition 167 | * @return a map of target methods to target contexts 168 | */ 169 | public Map> getTargets(CallSite callSite) { 170 | return transitions.get(callSite); 171 | } 172 | 173 | /** 174 | * Returns an unmodifiable view of context transitions. 175 | * 176 | * @return an unmodifiable view of context transitions 177 | */ 178 | public Map,Map>> getTransitions() { 179 | return Collections.unmodifiableMap(this.transitions); 180 | } 181 | 182 | /** 183 | * Computes a reachable set of value contexts from a particular 184 | * source by traversing the context transition table. 185 | * 186 | * Note that the source context itself is only reachable from 187 | * itself if it there is a recursive call to it (i.e. a context 188 | * is not reachable to itself by default). 189 | * 190 | * @param source the source context 191 | * @return a set of contexts reachable from source 192 | */ 193 | public Set> reachableSet(Context source, boolean ignoreFree) { 194 | // The result set 195 | Set> reachableContexts = new HashSet>(); 196 | 197 | // Maintain a stack of contexts to process 198 | Stack> stack = new Stack>(); 199 | // Initialise it with the source 200 | stack.push(source); 201 | 202 | // Now recursively (using stacks) mark reachable contexts 203 | while (stack.isEmpty() == false) { 204 | // Get the next item to process 205 | source = stack.pop(); 206 | // Add successors 207 | if (callSitesOfContexts.containsKey(source)) { 208 | // The above check is there because methods with no calls have no entry 209 | for (CallSite callSite : callSitesOfContexts.get(source)) { 210 | // Don't worry about DEFAULT edges 211 | if (defaultCallSites.contains(callSite)) { 212 | continue; 213 | } 214 | for (M method : transitions.get(callSite).keySet()) { 215 | Context target = transitions.get(callSite).get(method); 216 | // Don't process the same element twice 217 | if (reachableContexts.contains(target) == false) { 218 | // Are we ignoring free contexts? 219 | if (ignoreFree && target.isFreed()) { 220 | continue; 221 | } 222 | // Mark reachable 223 | reachableContexts.add(target); 224 | // Add it's successors also later 225 | stack.push(target); 226 | } 227 | } 228 | } 229 | } 230 | 231 | } 232 | return reachableContexts; 233 | } 234 | 235 | 236 | 237 | } 238 | -------------------------------------------------------------------------------- /src/main/java/vasco/DataFlowSolution.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * A mapping of program points to results of data flow analysis. 24 | * 25 | *

This simple mapping does not parametrise solutions with a 26 | * context object, and is thus context-insensitive. The results of a 27 | * context-sensitive inter-procedural analysis can be reduced to this 28 | * form by merging results for the same program point across all 29 | * contexts, giving what is known as the Meet-Over-Valid-Paths solution.

30 | * 31 | * @author Rohan Padhye 32 | * 33 | * @param the type of a node in the CFG 34 | * @param
the type of a data flow value 35 | * 36 | */ 37 | public class DataFlowSolution { 38 | 39 | /** A map of nodes to data flow values at the entry of the node. */ 40 | private Map inValues; 41 | 42 | /** A map of nodes to data flow values at the exit of the node. */ 43 | private Map outValues; 44 | 45 | /** 46 | * Constructs a data flow solution with the given IN and OUT values. 47 | * 48 | * @param inValues a map of nodes to data flow values at their entry 49 | * @param outValues a map of nodes to data flow values at their exit 50 | */ 51 | public DataFlowSolution(Map inValues, Map outValues) { 52 | this.inValues = inValues; 53 | this.outValues = outValues; 54 | } 55 | 56 | /** 57 | * Returns the data flow value at the entry of a node. 58 | * 59 | * @param node a program point 60 | * @return the data flow value at the entry of node 61 | * 62 | */ 63 | public A getValueBefore(N node) { 64 | return inValues.get(node); 65 | } 66 | 67 | /** 68 | * Returns the data flow value at the exit of a node. 69 | * 70 | * @param node a program point 71 | * @return the data flow value at the exit of node 72 | * 73 | */ 74 | public A getValueAfter(N node) { 75 | return outValues.get(node); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/vasco/ForwardInterProceduralAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.LinkedList; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | /** 25 | * A generic forward-flow inter-procedural analysis which is fully 26 | * context-sensitive. 27 | * 28 | *

29 | * This class essentially captures a forward data flow problem which can be 30 | * solved using the context-sensitive inter-procedural analysis framework as 31 | * described in {@link InterProceduralAnalysis}. 32 | *

33 | * 34 | *

35 | * This is the class that client analyses will extend in order to perform 36 | * forward-flow inter-procedural analysis. 37 | *

38 | * 39 | * @author Rohan Padhye 40 | * 41 | * @param the type of a method 42 | * @param the type of a node in the CFG 43 | * @param
the type of a data flow value 44 | */ 45 | public abstract class ForwardInterProceduralAnalysis extends InterProceduralAnalysis { 46 | 47 | /** Constructs a new forward-flow inter-procedural analysis. */ 48 | public ForwardInterProceduralAnalysis() { 49 | // Kick-up to the super with the FORWARD direction. 50 | super(false); 51 | 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | @Override 58 | public void doAnalysis() { 59 | 60 | // Initial contexts 61 | for (M method : programRepresentation().getEntryPoints()) { 62 | if (programRepresentation().isPhantomMethod(method)) { 63 | initContextForPhantomMethod(method, boundaryValue(method)); 64 | } else { 65 | initContext(method, boundaryValue(method)); 66 | } 67 | } 68 | 69 | // Perform work-list based analysis 70 | while (!worklist.isEmpty()) { 71 | // Get the newest context on the work-list 72 | Context currentContext = worklist.last(); 73 | 74 | // If this context has no more nodes to analyze, then take it out of the work-list 75 | if (currentContext.getWorkList().isEmpty()) { 76 | currentContext.markAnalysed(); 77 | worklist.remove(currentContext); 78 | continue; 79 | } 80 | 81 | 82 | // Remove the next node to process from the context's work-list 83 | N node = currentContext.getWorkList().pollFirst(); 84 | 85 | if (node != null) { 86 | // Compute the IN data flow value (only for non-entry units). 87 | List predecessors = currentContext.getControlFlowGraph().getPredsOf(node); 88 | if (predecessors.size() != 0) { 89 | // Initialise to the TOP value 90 | A in = topValue(); 91 | // Merge OUT values of all predecessors 92 | for (N pred : predecessors) { 93 | A predOut = currentContext.getValueAfter(pred); 94 | in = meet(in, predOut); 95 | } 96 | // Set the IN value at the node to the result 97 | currentContext.setValueBefore(node, in); 98 | } 99 | 100 | // Store the value of OUT before the flow function is processed. 101 | A prevOut = currentContext.getValueAfter(node); 102 | 103 | // Get the value of IN 104 | A in = currentContext.getValueBefore(node); 105 | 106 | if (verbose) { 107 | System.out.println("IN = " + in); 108 | System.err.println(node); 109 | } 110 | 111 | // Now to compute the OUT value 112 | A out; 113 | 114 | // Handle flow functions depending on whether this is a call statement or not 115 | if (programRepresentation().isCall(node)) { 116 | 117 | out = topValue(); 118 | boolean hit = false; 119 | if (!programRepresentation().resolveTargets(currentContext.getMethod(), node).isEmpty()) { 120 | for (M targetMethod : programRepresentation().resolveTargets(currentContext.getMethod(), node)) { 121 | A entryValue = callEntryFlowFunction(currentContext, targetMethod, node, in); 122 | 123 | CallSite callSite = new CallSite(currentContext, node); 124 | 125 | // Check if the called method has a context associated with this entry flow: 126 | Context targetContext = getContext(targetMethod, entryValue); 127 | // If not, then set 'targetContext' to a new context with the given entry flow. 128 | if (targetContext == null) { 129 | targetContext = initContext(targetMethod, entryValue); 130 | if (verbose) { 131 | System.out.println("[NEW] X" + currentContext + " -> X" + targetContext + " " + targetMethod + " "); 132 | System.out.println("ENTRY(X" + targetContext + ") = " + entryValue); 133 | } 134 | 135 | } 136 | 137 | // Store the transition from the calling context and site to the called context. 138 | contextTransitions.addTransition(callSite, targetContext); 139 | 140 | // Check if the target context has been analysed (surely not if it is just newly made): 141 | if (targetContext.isAnalysed()) { 142 | hit = true; 143 | A exitValue = targetContext.getExitValue(); 144 | if (verbose) { 145 | System.out.println("[HIT] X" + currentContext + " -> X" + targetContext + " " + targetMethod + " "); 146 | System.out.println("EXIT(X" + targetContext + ") = " + exitValue); 147 | } 148 | A returnedValue = callExitFlowFunction(currentContext, targetMethod, node, exitValue); 149 | out = meet(out, returnedValue); 150 | } 151 | } 152 | 153 | // If there was at least one hit, continue propagation 154 | if (hit) { 155 | A localValue = callLocalFlowFunction(currentContext, node, in); 156 | out = meet(out, localValue); 157 | } 158 | else { 159 | out = callLocalFlowFunction(currentContext, node, in); 160 | } 161 | } 162 | else 163 | { 164 | // handle phantom method 165 | out = callLocalFlowFunction(currentContext, node, in); 166 | } 167 | } else { 168 | out = normalFlowFunction(currentContext, node, in); 169 | } 170 | if (verbose) { 171 | System.out.println("OUT = " + out); 172 | System.out.println("---------------------------------------"); 173 | } 174 | 175 | 176 | // Merge with previous OUT to force monotonicity (harmless if flow functions are monotinic) 177 | out = meet(out, prevOut); 178 | 179 | // Set the OUT value 180 | currentContext.setValueAfter(node, out); 181 | 182 | // If OUT has changed... 183 | if (out.equals(prevOut) == false) { 184 | // Then add successors to the work-list. 185 | for (N successor : currentContext.getControlFlowGraph().getSuccsOf(node)) { 186 | currentContext.getWorkList().add(successor); 187 | } 188 | } 189 | // If the unit is in TAILS, then we have at least one 190 | // path to the end of the method, so add the NULL unit 191 | if (currentContext.getControlFlowGraph().getTails().contains(node)) { 192 | currentContext.getWorkList().add(null); 193 | } 194 | } else { 195 | // NULL unit, which means the end of the method. 196 | assert (currentContext.getWorkList().isEmpty()); 197 | 198 | // Exit value is the merge of the OUTs of the tail nodes. 199 | A exitValue = topValue(); 200 | for (N tailNode : currentContext.getControlFlowGraph().getTails()) { 201 | A tailOut = currentContext.getValueAfter(tailNode); 202 | exitValue = meet(exitValue, tailOut); 203 | } 204 | 205 | // Set the exit value of the context. 206 | currentContext.setExitValue(exitValue); 207 | 208 | // Mark this context as analysed at least once. 209 | currentContext.markAnalysed(); 210 | 211 | // Add callers to work-list, if any 212 | Set> callers = contextTransitions.getCallers(currentContext); 213 | if (callers != null) { 214 | for (CallSite callSite : callers) { 215 | // Extract the calling context and node from the caller site. 216 | Context callingContext = callSite.getCallingContext(); 217 | N callNode = callSite.getCallNode(); 218 | // Add the calling unit to the calling context's node work-list. 219 | callingContext.getWorkList().add(callNode); 220 | // Ensure that the calling context is on the context work-list. 221 | worklist.add(callingContext); 222 | } 223 | } 224 | 225 | // Free memory on-the-fly if not needed 226 | if (freeResultsOnTheFly) { 227 | Set> reachableContexts = contextTransitions.reachableSet(currentContext, true); 228 | // If any reachable contexts exist on the work-list, then we cannot free memory 229 | boolean canFree = true; 230 | for (Context reachableContext : reachableContexts) { 231 | if (worklist.contains(reachableContext)) { 232 | canFree = false; 233 | break; 234 | } 235 | } 236 | // If no reachable contexts on the stack, then free memory associated 237 | // with this context 238 | if (canFree) { 239 | for (Context reachableContext : reachableContexts) { 240 | reachableContext.freeMemory(); 241 | } 242 | } 243 | } 244 | } 245 | 246 | } 247 | 248 | // Sanity check 249 | for (List> contextList : contexts.values()) { 250 | for (Context context : contextList) { 251 | if (context.isAnalysed() == false) { 252 | System.err.println("*** ATTENTION ***: Only partial analysis of X" + context + 253 | " " + context.getMethod()); 254 | } 255 | } 256 | } 257 | } 258 | 259 | /** 260 | * Creates a new value for phantom method 261 | * 262 | * @param method 263 | * @param entryValue 264 | * @return 265 | */ 266 | protected Context initContextForPhantomMethod(M method, A entryValue) { 267 | Context context = new Context(method); 268 | context.setEntryValue(entryValue); 269 | context.setExitValue(copy(entryValue)); 270 | context.markAnalysed(); 271 | return context; 272 | } 273 | 274 | /** 275 | * Creates a new value context and initialises data flow values for its nodes. 276 | * 277 | *

278 | * The following steps are performed: 279 | *

    280 | *
  1. Construct the context.
  2. 281 | *
  3. Initialise IN/OUT for all nodes and add them to the work-list
  4. 282 | *
  5. Initialise the IN of entry points with a copy of the given entry value.
  6. 283 | *
  7. Add this new context to the given method's mapping.
  8. 284 | *
  9. Add this context to the global work-list.
  10. 285 | *
286 | *

287 | * 288 | * @param method the method whose context to create 289 | * @param entryValue the data flow value at the entry of this method 290 | */ 291 | protected Context initContext(M method, A entryValue) { 292 | // Construct the context 293 | Context context = new Context(method, programRepresentation().getControlFlowGraph(method), false); 294 | 295 | // Initialise IN/OUT for all nodes and add them to the work-list 296 | for (N unit : context.getControlFlowGraph()) { 297 | context.setValueBefore(unit, topValue()); 298 | context.setValueAfter(unit, topValue()); 299 | context.getWorkList().add(unit); 300 | } 301 | 302 | // Now, initialise the IN of entry points with a copy of the given entry value. 303 | context.setEntryValue(copy(entryValue)); 304 | for (N unit : context.getControlFlowGraph().getHeads()) { 305 | context.setValueBefore(unit, copy(entryValue)); 306 | } 307 | context.setExitValue(topValue()); 308 | 309 | // Add this new context to the given method's mapping. 310 | if (!contexts.containsKey(method)) { 311 | contexts.put(method, new LinkedList>()); 312 | } 313 | contexts.get(method).add(context); 314 | 315 | // Add this context to the global work-list 316 | worklist.add(context); 317 | 318 | return context; 319 | 320 | } 321 | 322 | /** 323 | * Processes the intra-procedural flow function of a statement that does 324 | * not contain a method call. 325 | * 326 | * @param context the value context at the call-site 327 | * @param node the statement whose flow function to process 328 | * @param inValue the data flow value before the statement 329 | * @return the data flow value after the statement 330 | */ 331 | public abstract A normalFlowFunction(Context context, N node, A inValue); 332 | 333 | /** 334 | * Processes the inter-procedural flow function for a method call at 335 | * the start of the call, to handle parameters. 336 | * 337 | * @param context the value context at the call-site 338 | * @param targetMethod the target (or one of the targets) of this call site 339 | * @param node the statement containing the method call 340 | * @param inValue the data flow value before the call 341 | * @return the data flow value at the entry to the called procedure 342 | */ 343 | public abstract A callEntryFlowFunction(Context context, M targetMethod, N node, A inValue); 344 | 345 | /** 346 | * Processes the inter-procedural flow function for a method call at the 347 | * end of the call, to handle return values. 348 | * 349 | * @param context the value context at the call-site 350 | * @param targetMethod the target (or one of the targets) of this call site 351 | * @param node the statement containing the method call 352 | * @param exitValue the data flow value at the exit of the called procedure 353 | * @return the data flow value after the call (returned component) 354 | */ 355 | public abstract A callExitFlowFunction(Context context, M targetMethod, N node, A exitValue); 356 | 357 | /** 358 | * 359 | * Processes the intra-procedural flow function for a method call at the 360 | * call-site itself, to handle propagation of local values that are not 361 | * involved in the call. 362 | * 363 | * @param context the value context at the call-site 364 | * @param node the statement containing the method call 365 | * @param inValue the data flow value before the call 366 | * @return the data flow value after the call (local component) 367 | */ 368 | public abstract A callLocalFlowFunction(Context context, N node, A inValue); 369 | 370 | 371 | 372 | 373 | } 374 | 375 | -------------------------------------------------------------------------------- /src/main/java/vasco/InterProceduralAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.NavigableSet; 26 | import java.util.Set; 27 | import java.util.TreeSet; 28 | 29 | /** 30 | * A generic inter-procedural analysis which is fully context-sensitive. 31 | * 32 | *

33 | * This class is a base for forward and backward inter-procedural analysis 34 | * classes. This inter-procedural analysis framework is fully context 35 | * sensitive even in the presence of recursion and uses data flow values 36 | * reaching a method to distinguish contexts. 37 | *

38 | * 39 | * @author Rohan Padhye 40 | * 41 | * @param the type of a method 42 | * @param the type of a node in the CFG 43 | * @param
the type of a data flow value 44 | * 45 | * @see Context 46 | * @see ContextTransitionTable 47 | * @see CallSite 48 | */ 49 | public abstract class InterProceduralAnalysis { 50 | 51 | /** A work-list of contexts to process. */ 52 | protected final NavigableSet> worklist; 53 | 54 | /** A mapping from methods to a list of contexts for quick lookups. */ 55 | protected final Map>> contexts; 56 | 57 | /** 58 | * A record of transitions from calling context and call-site to 59 | * called method and called context. 60 | */ 61 | protected final ContextTransitionTable contextTransitions; 62 | 63 | 64 | /** 65 | * true if the direction of analysis is backward, or false 66 | * if it is forward. 67 | */ 68 | protected final boolean reverse; 69 | 70 | /** 71 | * A flag, if set, directs the analysis to free memory storing 72 | * data flow values of individual statements once a context has 73 | * been analysed and would not be required to be re-analysed. 74 | * 75 | *

This setting is only useful for analyses that aggregate secondary 76 | * results on the fly (e.g. call graph analysis that can do away with 77 | * points-to information once the calls for a particular context have 78 | * been resolved). This is not safe for use in analyses whose results 79 | * will be directly used later (e.g. liveness analysis).

80 | * 81 | *

Memory is freed when a context is removed from the work-list and no context 82 | * reachable from it in the transition table is also on the work-list. This 83 | * ensures that the removed context will not be added again on the work-list 84 | * for re-analysis of any statement.

85 | * 86 | *

Note that the data flow values at the entry/exit of the context are 87 | * not freed, and hence it is still used to terminate recursion or as a cache 88 | * hit for arbitrary call sites with same values.

89 | * 90 | *

The default value for this flag is false.

91 | * 92 | */ 93 | protected boolean freeResultsOnTheFly; 94 | 95 | /** 96 | * Whether to print information about contexts. 97 | */ 98 | protected boolean verbose; 99 | 100 | /** 101 | * Constructs a new inter-procedural analysis. 102 | * 103 | * @param reverse true if the analysis is in the reverse direction, 104 | * false if it is in the forward direction 105 | */ 106 | public InterProceduralAnalysis(boolean reverse) { 107 | 108 | // Set direction 109 | this.reverse = reverse; 110 | 111 | // Initialise map of methods to contexts. 112 | contexts = new HashMap>>(); 113 | 114 | // Initialise context transition table 115 | contextTransitions = new ContextTransitionTable(); 116 | 117 | // Initialise the work-list 118 | worklist = new TreeSet>(); 119 | 120 | } 121 | 122 | /** 123 | * Returns the initial data flow value at the program entry points. For 124 | * forward analyses this is the IN value at the ENTRY to each entry method, 125 | * while for backward analyses this is the OUT value at the EXIT to each 126 | * entry method. 127 | * 128 | *

Note that this method will be called exactly once per entry point 129 | * specified by the program representation.

130 | * 131 | * @param entryPoint an entry point specified by the program representation 132 | * @return the data flow value at the boundary 133 | * 134 | * @see ProgramRepresentation#getEntryPoints() 135 | */ 136 | public abstract A boundaryValue(M entryPoint); 137 | 138 | /** 139 | * Returns a copy of the given data flow value. 140 | * 141 | * @param src the data flow value to copy 142 | * @return a new data flow value which is a copy of the argument 143 | */ 144 | public abstract A copy(A src); 145 | 146 | /** 147 | * Performs the actual data flow analysis. 148 | * 149 | *

150 | * A work-list of contexts is maintained, each with it's own work-list of CFG nodes 151 | * to process. For each node removed from the work-list of the newest context, 152 | * the meet of values along incoming edges (in the direction of analysis) is computed and 153 | * then the flow function is processed depending on whether the node contains a call 154 | * or not. If the resulting data flow value has changed, then nodes along outgoing edges 155 | * (in the direction of analysis) are also added to the work-list.

156 | * 157 | *

158 | * Analysis starts with the context for the program entry points with the given 159 | * boundary values and ends when the work-list is empty. 160 | *

161 | * 162 | *

See the SOAP '13 paper for the full algorithm in Figure 1.

163 | * 164 | */ 165 | public abstract void doAnalysis(); 166 | 167 | 168 | /** 169 | * Returns the callers of a value context. 170 | * 171 | * @param target the value context 172 | * @return the call-sites which transition to the value context 173 | */ 174 | public Set> getCallers(Context target) { 175 | return this.contextTransitions.getCallers(target); 176 | } 177 | 178 | /** 179 | * Retrieves a particular value context if it has been constructed. 180 | * 181 | * @param method the method whose value context to find 182 | * @param value the data flow value at the entry (forward flow) or exit 183 | * (backward flow) of the method 184 | * @return the value context, if one is found with the given parameters, 185 | * or null otherwise 186 | */ 187 | public Context getContext(M method, A value) { 188 | // If this method does not have any contexts, then we'll have to return nothing. 189 | if (!contexts.containsKey(method)) { 190 | return null; 191 | } 192 | // Otherwise, look for a context in this method's list with the given value. 193 | if (reverse) { 194 | // Backward flow, so check for EXIT FLOWS 195 | for (Context context : contexts.get(method)) { 196 | if (value.equals(context.getExitValue())) { 197 | return context; 198 | } 199 | } 200 | } else { 201 | // Forward flow, so check for ENTRY FLOWS 202 | for (Context context : contexts.get(method)) { 203 | if (value.equals(context.getEntryValue())) { 204 | return context; 205 | } 206 | } 207 | } 208 | // If nothing found return null. 209 | return null; 210 | } 211 | 212 | /** 213 | * Returns a list of value contexts constructed for a given method. 214 | * 215 | * @param method the method whose contexts to retrieve 216 | * @return an unmodifiable list of value contexts of the given method 217 | */ 218 | public List> getContexts(M method) { 219 | if (contexts.containsKey(method)) { 220 | return Collections.unmodifiableList(contexts.get(method)); 221 | } else { 222 | return Collections.unmodifiableList(new LinkedList>()); 223 | } 224 | } 225 | 226 | /** 227 | * Returns a reference to the context transition table used by this analysis. 228 | * 229 | * @return a reference to the context transition table used by this analysis 230 | */ 231 | public ContextTransitionTable getContextTransitionTable() { 232 | return contextTransitions; 233 | } 234 | 235 | /** 236 | * Returns a meet-over-valid-paths solution by merging data flow 237 | * values across contexts for each program point. 238 | * 239 | *

This method should not be invoked if the flag 240 | * {@link #freeResultsOnTheFly} had been set during analysis.

241 | * 242 | * @return a meet-over-valid-paths data flow solution 243 | */ 244 | public DataFlowSolution getMeetOverValidPathsSolution() { 245 | Map inValues = new HashMap(); 246 | Map outValues = new HashMap(); 247 | // Merge over all contexts 248 | for (M method : contexts.keySet()) { 249 | for (N node : programRepresentation().getControlFlowGraph(method)) { 250 | A in = topValue(); 251 | A out = topValue(); 252 | for (Context context : contexts.get(method)) { 253 | in = meet(in, context.getValueBefore(node)); 254 | out = meet(out, context.getValueAfter(node)); 255 | } 256 | inValues.put(node, in); 257 | outValues.put(node, out); 258 | } 259 | } 260 | // Return data flow solution 261 | return new DataFlowSolution(inValues, outValues); 262 | } 263 | 264 | /** 265 | * Returns all methods for which at least one context was created. 266 | * @return an unmodifiable set of analysed methods 267 | */ 268 | public Set getMethods() { 269 | return Collections.unmodifiableSet(contexts.keySet()); 270 | } 271 | 272 | /** 273 | * Returns the target of a call-site. 274 | * 275 | * @param callSite the call-site whose targets to retrieve 276 | * @return a map of target methods to their respective contexts 277 | */ 278 | public Map> getTargets(CallSite callSite) { 279 | return this.contextTransitions.getTargets(callSite); 280 | } 281 | 282 | /** 283 | * Returns the meet of two data flow values. 284 | * 285 | * @param op1 the first operand 286 | * @param op2 the second operand 287 | * @return a new data flow which is the result of the meet operation of the 288 | * two operands 289 | */ 290 | public abstract A meet(A op1, A op2); 291 | 292 | /** 293 | * Returns a program representation on top of which the inter-procedural 294 | * analysis runs. The program representation is used to build control 295 | * flow graphs of individual procedures and resolve targets of virtual 296 | * call sites. 297 | * 298 | * @return The program representation underlying this analysis 299 | */ 300 | public abstract ProgramRepresentation programRepresentation(); 301 | 302 | /** 303 | * Returns the default data flow value (lattice top). 304 | * 305 | * @return the default data flow value (lattice top) 306 | */ 307 | public abstract A topValue(); 308 | 309 | } 310 | -------------------------------------------------------------------------------- /src/main/java/vasco/OldForwardInterProceduralAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.Collections; 21 | import java.util.Iterator; 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Set; 25 | import java.util.Stack; 26 | 27 | /** 28 | * A generic forward-flow inter-procedural analysis which is fully context-sensitive. 29 | * 30 | *

31 | * This class essentially captures a forward data flow problem which can be 32 | * solved using the context-sensitive inter-procedural analysis framework as 33 | * described in {@link InterProceduralAnalysis}. 34 | *

35 | * 36 | *

37 | * This is the class that client analyses will extend in order to perform 38 | * forward-flow inter-procedural analysis. 39 | *

40 | * 41 | * @author Rohan Padhye 42 | * 43 | * @param the type of a method 44 | * @param the type of a node in the CFG 45 | * @param
the type of a data flow value 46 | * 47 | * @deprecated This is the old API from the initial SOAP '13 submission without call/return flow functions. 48 | * It is only here for a temporary period while the {@link vasco.callgraph.PointsToAnalysis PointsToAnalysis} class is migrated to the new API. 49 | * After that work is done, this class will be permanently removed from VASCO. 50 | */ 51 | public abstract class OldForwardInterProceduralAnalysis extends InterProceduralAnalysis { 52 | 53 | /** Constructs a new forward-flow inter-procedural analysis. */ 54 | public OldForwardInterProceduralAnalysis() { 55 | // Kick-up to the super with the FORWARD direction. 56 | super(false); 57 | analysisStack = new Stack>(); 58 | } 59 | 60 | protected Stack> analysisStack; 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | @Override 66 | public void doAnalysis() { 67 | 68 | // Initialise the MAIN context 69 | for (M entryPoint : programRepresentation().getEntryPoints()) { 70 | Context context = new Context(entryPoint, programRepresentation().getControlFlowGraph(entryPoint), false); 71 | A boundaryInformation = boundaryValue(entryPoint); 72 | initContext(context, boundaryInformation); 73 | 74 | } 75 | 76 | // Stack-of-work-lists data flow analysis. 77 | while (!analysisStack.isEmpty()) { 78 | // Get the context at the top of the stack. 79 | Context context = analysisStack.peek(); 80 | 81 | // Either analyse the next pending unit or pop out of the method 82 | if (!context.getWorkList().isEmpty()) { 83 | // work-list contains items; So the next unit to analyse. 84 | N unit = context.getWorkList().pollFirst(); 85 | 86 | if (unit != null) { 87 | // Compute the IN data flow value (only for non-entry units). 88 | List predecessors = context.getControlFlowGraph().getPredsOf(unit); 89 | if (predecessors.size() != 0) { 90 | // Merge all the OUT values of the predecessors 91 | Iterator predIterator = predecessors.iterator(); 92 | // Initialise IN to the OUT value of the first predecessor 93 | A in = context.getValueAfter(predIterator.next()); 94 | // Then, merge OUT of remaining predecessors with the 95 | // intermediate IN value 96 | while (predIterator.hasNext()) { 97 | A predOut = context.getValueAfter(predIterator.next()); 98 | in = meet(in, predOut); 99 | } 100 | // Set the IN value at the context 101 | context.setValueBefore(unit, in); 102 | } 103 | 104 | // Store the value of OUT before the flow function is processed. 105 | A prevOut = context.getValueAfter(unit); 106 | 107 | // Get the value of IN 108 | A in = context.getValueBefore(unit); 109 | 110 | // Now perform the flow function. 111 | A out = flowFunction(context, unit, in); 112 | 113 | // If the result is null, then no change 114 | if (out == null) 115 | out = prevOut; 116 | 117 | // Set the OUT value 118 | context.setValueAfter(unit, out); 119 | 120 | // If the flow function was applied successfully and the OUT changed... 121 | if (out.equals(prevOut) == false) { 122 | // Then add successors to the work-list. 123 | for (N successor : context.getControlFlowGraph().getSuccsOf(unit)) { 124 | context.getWorkList().add(successor); 125 | } 126 | // If the unit is in TAILS, then we have at least one 127 | // path to the end of the method, so add the NULL unit 128 | if (context.getControlFlowGraph().getTails().contains(unit)) { 129 | context.getWorkList().add(null); 130 | } 131 | } 132 | } else { 133 | // NULL unit, which means the end of the method. 134 | assert (context.getWorkList().isEmpty()); 135 | 136 | // Exit flow value is the merge of the OUTs of the tail nodes. 137 | A exitFlow = topValue(); 138 | for (N tail : context.getControlFlowGraph().getTails()) { 139 | A tailOut = context.getValueAfter(tail); 140 | exitFlow = meet(exitFlow, tailOut); 141 | } 142 | // Set the exit flow of the context. 143 | context.setExitValue(exitFlow); 144 | 145 | // Mark this context as analysed at least once. 146 | context.markAnalysed(); 147 | 148 | // Add return nodes to stack (only if there were callers). 149 | Set> callersSet = contextTransitions.getCallers(context); 150 | if (callersSet != null) { 151 | List> callers = new LinkedList>(callersSet); 152 | // Sort the callers in ascending order of their ID so that 153 | // the largest ID is on top of the stack 154 | Collections.sort(callers); 155 | for (CallSite callSite : callers) { 156 | // Extract the calling context and unit from the caller site. 157 | Context callingContext = callSite.getCallingContext(); 158 | N callingNode = callSite.getCallNode(); 159 | // Add the calling unit to the calling context's work-list. 160 | callingContext.getWorkList().add(callingNode); 161 | // Ensure that the calling context is on the analysis stack, 162 | // and if not, push it on to the stack. 163 | if (!analysisStack.contains(callingContext)) { 164 | analysisStack.push(callingContext); 165 | } 166 | } 167 | } 168 | 169 | // Free memory on-the-fly if not needed 170 | if (freeResultsOnTheFly) { 171 | Set> reachableContexts = contextTransitions.reachableSet(context, true); 172 | // If any reachable contexts exist on the stack, then we cannot free memory 173 | boolean canFree = true; 174 | for (Context reachableContext : reachableContexts) { 175 | if (analysisStack.contains(reachableContext)) { 176 | canFree = false; 177 | break; 178 | } 179 | } 180 | // If no reachable contexts on the stack, then free memory associated 181 | // with this context 182 | if (canFree) { 183 | for (Context reachableContext : reachableContexts) { 184 | reachableContext.freeMemory(); 185 | } 186 | } 187 | } 188 | } 189 | } else { 190 | // If work-list is empty, then remove it from the analysis. 191 | analysisStack.remove(context); 192 | } 193 | } 194 | 195 | // Sanity check 196 | for (List> contextList : contexts.values()) { 197 | for (Context context : contextList) { 198 | if (context.isAnalysed() == false) { 199 | System.err.println("*** ATTENTION ***: Only partial analysis of X" + context + 200 | " " + context.getMethod()); 201 | } 202 | } 203 | } 204 | } 205 | 206 | /** 207 | * Creates a new context and initialises data flow values. 208 | * 209 | *

210 | * The following steps are performed: 211 | *

    212 | *
  1. Initialise all nodes to default flow value (lattice top).
  2. 213 | *
  3. Initialise the entry nodes (heads) with a copy of the entry value.
  4. 214 | *
  5. Add entry points to work-list.
  6. 215 | *
  7. Push this context on the top of the analysis stack.
  8. 216 | *
217 | *

218 | * 219 | * @param context the context to initialise 220 | * @param entryValue the data flow value at the entry of this method 221 | */ 222 | protected void initContext(Context context, A entryValue) { 223 | // Get the method 224 | M method = context.getMethod(); 225 | 226 | // First initialise all points to default flow value. 227 | for (N unit : context.getControlFlowGraph()) { 228 | context.setValueBefore(unit, topValue()); 229 | context.setValueAfter(unit, topValue()); 230 | } 231 | 232 | // Now, initialise entry points with a copy of the given entry flow. 233 | context.setEntryValue(copy(entryValue)); 234 | for (N unit : context.getControlFlowGraph().getHeads()) { 235 | context.setValueBefore(unit, copy(entryValue)); 236 | // Add entry points to work-list 237 | context.getWorkList().add(unit); 238 | } 239 | 240 | // Add this new context to the given method's mapping. 241 | if (!contexts.containsKey(method)) { 242 | contexts.put(method, new LinkedList>()); 243 | } 244 | contexts.get(method).add(context); 245 | 246 | // Push this context on the top of the analysis stack. 247 | analysisStack.add(context); 248 | 249 | } 250 | 251 | /** 252 | * Processes a call statement. 253 | * 254 | *

255 | * Retrieves a value context for the callee if one exists with the given 256 | * entry value, or else creates a new one and adds the transition to 257 | * the context transition table. 258 | *

259 | * 260 | *

261 | * If the callee context has already been analysed, returns the resulting 262 | * exit value. For newly created contexts the result would be null, 263 | * as they are obviously not analysed even once. 264 | *

265 | * 266 | *

267 | * Note that this method is not directly called by {@link #doAnalysis() doAnalysis}, but 268 | * is instead called by {@link #flowFunction(Context, Object, Object) flowFunction} when a method 269 | * call statement is encountered. The reason for this design decision is 270 | * that client analyses may want their own setup and tear down sequences 271 | * before a call is made (similar to edge flow functions at the call and 272 | * return site). Also, analyses may want to choose which method call to 273 | * process at an invoke statement in the case of virtual calls (e.g. a 274 | * points-to analysis may build the call-graph on-the-fly). 275 | *

276 | * 277 | *

Therefore, it is the responsibility of the client analysis to detect 278 | * an invoke expression when implementing {@link #flowFunction(Context, Object, Object) flowFunction}, 279 | * and suitably invoke {@link #processCall(Context, Object, Object, Object) processCall} with 280 | * the input data flow value which may be different from the IN/OUT value of the node due to 281 | * handling of arguments, etc. Similarly, the result of {@link #processCall(Context, Object, Object, Object) processCall} 282 | * may be modified by the client to handle return values, etc. before returning from {@link #flowFunction(Context, Object, Object) flowFunction}. 283 | * Ideally, {@link #flowFunction(Context, Object, Object) flowFunction} should return 284 | * null if and only if {@link #processCall(Context, Object, Object, Object) processCall} 285 | * returns null. 286 | * 287 | * @param callerContext the analysis context at the call-site 288 | * @param callNode the calling statement 289 | * @param method the method being called 290 | * @param entryValue the data flow value at the entry of the called method. 291 | * @return the data flow value at the exit of the called method, 292 | * if available, or null if unavailable. 293 | */ 294 | protected A processCall(Context callerContext, N callNode, M method, A entryValue) { 295 | CallSite callSite = new CallSite(callerContext, callNode); 296 | 297 | // Check if the called method has a context associated with this entry flow: 298 | Context calleeContext = getContext(method, entryValue); 299 | // If not, then set 'calleeContext' to a new context with the given entry flow. 300 | if (calleeContext == null) { 301 | calleeContext = new Context(method, programRepresentation().getControlFlowGraph(method), false); 302 | initContext(calleeContext, entryValue); 303 | if (verbose) { 304 | System.out.println("[NEW] X" + callerContext + " -> X" + calleeContext + " " + method + " "); 305 | } 306 | } 307 | 308 | // Store the transition from the calling context and site to the called context. 309 | contextTransitions.addTransition(callSite, calleeContext); 310 | 311 | // Check if 'caleeContext' has been analysed (surely not if it is just newly made): 312 | if (calleeContext.isAnalysed()) { 313 | if (verbose) { 314 | System.out.println("[HIT] X" + callerContext + " -> X" + calleeContext + " " + method + " "); 315 | } 316 | // If yes, then return the 'exitFlow' of the 'calleeContext'. 317 | return calleeContext.getExitValue(); 318 | } else { 319 | // If not, then return 'null'. 320 | return null; 321 | } 322 | } 323 | 324 | protected abstract A flowFunction(Context context, N unit, A in); 325 | 326 | } 327 | -------------------------------------------------------------------------------- /src/main/java/vasco/ProgramRepresentation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco; 19 | 20 | import java.util.List; 21 | 22 | import soot.toolkits.graph.DirectedGraph; 23 | 24 | /** 25 | * A wrapper for the API used by the underlying 26 | * intermediate representation over which inter-procedural analysis is to be 27 | * performed. 28 | * 29 | *

In particular, the program representation should specify program entry 30 | * points, build control flow graphs for a given method and resolve virtual 31 | * method calls.

32 | * 33 | * @author Rohan Padhye 34 | * 35 | * @param the type of a method 36 | * @param the type of a node in the CFG 37 | */ 38 | public interface ProgramRepresentation { 39 | 40 | /** 41 | * Returns a list of program entry points (methods). The entry points 42 | * may be static or non-static. 43 | * 44 | *

Client analyses implementing an {@link InterProceduralAnalysis} 45 | * must implement the 46 | * {@link InterProceduralAnalysis#boundaryValue(Object) boundaryValue} method 47 | * for each entry point specified by the program representation.

48 | * 49 | * @return a list of program entry points (methods) 50 | */ 51 | public List getEntryPoints(); 52 | 53 | /** 54 | * Returns an intra-procedural control-flow-graph for a given procedure (method). 55 | * 56 | *

The returned CFG may include exceptional control transfer in addition 57 | * to conditional and unconditional jumps, but does not include inter-procedural 58 | * call/return edges. Nodes containing method calls are treated like nodes 59 | * containing ordinary imperative instructions.

60 | * 61 | * @param method the method whose CFG to return 62 | * @return an intra-procedural control-flow-graph for a given procedure (method) 63 | */ 64 | public DirectedGraph getControlFlowGraph(M method); 65 | 66 | /** 67 | * Returns whether a given node contains a method call. 68 | * @param node a node in the control-flow graph 69 | * @return whether a given node contains a method call. 70 | */ 71 | public boolean isCall(N node); 72 | 73 | public boolean isPhantomMethod(M method); 74 | /** 75 | * Returns a list of target methods for call in the given node. 76 | * 77 | *

78 | * For static methods and special invocations (such as constructors), there 79 | * will be only one target, and hence a singleton list will be returned. 80 | *

81 | * 82 | *

83 | * For virtual calls, there may be multiple targets which are resolved using 84 | * an available call graph. 85 | *

86 | * 87 | *

88 | * If even a single target does not have an analysable method body (e.g. native 89 | * methods in Java), then null is returned, to indicate that the 90 | * targets cannot be properly resolved. 91 | * TODO: Native method flow functions? 92 | *

93 | * 94 | * @param callerMethod the method in which the call statement originates 95 | * @param callNode the node containing the call statement 96 | * @return a list of methods which are the target of this call, if their bodies 97 | * are available, or else null in the case of native targets 98 | */ 99 | public List resolveTargets(M callerMethod, N callNode); 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/vasco/callgraph/CallGraphTransformer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.callgraph; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.HashMap; 23 | import java.util.Iterator; 24 | import java.util.Map; 25 | import java.util.Set; 26 | 27 | import soot.Kind; 28 | import soot.Scene; 29 | import soot.SceneTransformer; 30 | import soot.SootMethod; 31 | import soot.Unit; 32 | import soot.jimple.InvokeExpr; 33 | import soot.jimple.Stmt; 34 | import soot.jimple.toolkits.callgraph.CallGraph; 35 | import soot.jimple.toolkits.callgraph.ContextSensitiveCallGraph; 36 | import soot.jimple.toolkits.callgraph.ContextSensitiveEdge; 37 | import soot.jimple.toolkits.callgraph.Edge; 38 | import vasco.CallSite; 39 | import vasco.Context; 40 | import vasco.ContextTransitionTable; 41 | 42 | /** 43 | * A Soot {@link SceneTransformer} for performing {@link PointsToAnalysis}. 44 | * 45 | * @author Rohan Padhye 46 | */ 47 | public class CallGraphTransformer extends SceneTransformer { 48 | 49 | private PointsToAnalysis pointsToAnalysis; 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | @SuppressWarnings("deprecation") 55 | @Override 56 | protected void internalTransform(String arg0, @SuppressWarnings("rawtypes") Map arg1) { 57 | // Perform the points-to analysis 58 | pointsToAnalysis = new PointsToAnalysis(); 59 | pointsToAnalysis.doAnalysis(); 60 | 61 | // Use the context transition table generated by the analysis to construct soot call graphs 62 | final ContextTransitionTable ctt = pointsToAnalysis.getContextTransitionTable(); 63 | 64 | 65 | // Initialize collections (for creating the soot context-sensitive call graph) 66 | final Set allMethods = pointsToAnalysis.getMethods(); 67 | final Map, Collection> csEdgesIntoContext = new HashMap, Collection>(); 68 | final Map, Collection> csEdgesOutOfContext = new HashMap, Collection>(); 69 | final Map, Collection> csEdgesOutOfCallSite = new HashMap, Collection>(); 70 | final Collection csEdges = new ArrayList(); 71 | 72 | // Initialize the context-insensitive call graph 73 | CallGraph callGraph = new CallGraph(); 74 | 75 | // Create soot-style edges for every context transition 76 | for (Map.Entry, Map>> e : ctt.getTransitions().entrySet()) { 77 | CallSite cs = e.getKey(); 78 | final Context sourceContext = cs.getCallingContext(); 79 | final SootMethod sourceMethod = sourceContext.getMethod(); 80 | final Stmt stmt = (Stmt) cs.getCallNode(); 81 | final Map> targets = e.getValue(); 82 | for (final SootMethod targetMethod : targets.keySet()) { 83 | final Context targetContext = targets.get(targetMethod); 84 | 85 | Kind k; 86 | if ("".equals(targetMethod.getName())) { 87 | k = Kind.CLINIT; 88 | } else if (stmt.containsInvokeExpr()) { 89 | k = Edge.ieToKind(stmt.getInvokeExpr()); 90 | } else { 91 | k = Kind.INVALID; 92 | } 93 | 94 | // The context-insensitive edge 95 | Edge cgEdge = new Edge(sourceMethod, stmt, targetMethod, k); 96 | 97 | // Add it to the context-insensitive call-graph 98 | callGraph.addEdge(cgEdge); 99 | 100 | // The context-sensitive edge 101 | ContextSensitiveEdge csEdge = new ContextSensitiveEdge() { 102 | 103 | @Override 104 | public Kind kind() { 105 | if ("".equals(targetMethod.getName())) { 106 | return Kind.CLINIT; 107 | } else if (stmt.containsInvokeExpr()) { 108 | return Edge.ieToKind(stmt.getInvokeExpr()); 109 | } else { 110 | return Kind.INVALID; 111 | } 112 | } 113 | 114 | @Override 115 | public SootMethod src() { 116 | return sourceMethod; 117 | } 118 | 119 | @Override 120 | public soot.Context srcCtxt() { 121 | return sourceContext; 122 | } 123 | 124 | @Override 125 | public Stmt srcStmt() { 126 | return (Stmt) stmt; 127 | } 128 | 129 | @Override 130 | public Unit srcUnit() { 131 | return stmt; 132 | } 133 | 134 | @Override 135 | public SootMethod tgt() { 136 | return targetMethod; 137 | } 138 | 139 | @Override 140 | public soot.Context tgtCtxt() { 141 | return targetContext; 142 | } 143 | 144 | }; 145 | 146 | // Add this in all the collections 147 | csEdges.add(csEdge); 148 | 149 | if (!csEdgesOutOfContext.containsKey(sourceContext)) 150 | csEdgesOutOfContext.put(sourceContext, new ArrayList()); 151 | csEdgesOutOfContext.get(sourceContext).add(csEdge); 152 | 153 | if (!csEdgesOutOfCallSite.containsKey(cs)) 154 | csEdgesOutOfCallSite.put(cs, new ArrayList()); 155 | csEdgesOutOfCallSite.get(cs).add(csEdge); 156 | 157 | if (!csEdgesIntoContext.containsKey(targetContext)) 158 | csEdgesIntoContext.put(targetContext, new ArrayList()); 159 | csEdgesIntoContext.get(targetContext).add(csEdge); 160 | 161 | 162 | 163 | } 164 | 165 | } 166 | 167 | // Set the scene's context-insensitive call-graph to what we just created 168 | Scene.v().setCallGraph(callGraph); 169 | 170 | // Set the scene's context-sensitive call graph to one that we construct on-the-fly using the above collections 171 | Scene.v().setContextSensitiveCallGraph(new ContextSensitiveCallGraph() { 172 | 173 | @SuppressWarnings("unchecked") 174 | private Context vContext(soot.Context sContext) { 175 | return (Context) sContext; 176 | } 177 | 178 | private CallSite vCallSite(soot.Context sContext, Unit unit) { 179 | return new CallSite(vContext(sContext), unit); 180 | } 181 | 182 | @Override 183 | public Iterator edgesOutOf(soot.Context sContext, SootMethod m, Unit stmt) { 184 | return csEdgesOutOfCallSite.get((vCallSite(sContext, stmt))).iterator(); 185 | } 186 | 187 | @Override 188 | public Iterator edgesOutOf(soot.Context sContext, SootMethod m) { 189 | return csEdgesOutOfContext.get(vContext(sContext)).iterator(); 190 | } 191 | 192 | @Override 193 | public Iterator edgesInto(soot.Context sContext, SootMethod m) { 194 | return csEdgesIntoContext.get(vContext(sContext)).iterator(); 195 | } 196 | 197 | @Override 198 | public Iterator edgeSources() { 199 | return allMethods.iterator(); 200 | } 201 | 202 | @Override 203 | public Iterator allEdges() { 204 | return csEdges.iterator(); 205 | } 206 | }); 207 | 208 | } 209 | 210 | /** 211 | * Returns a reference to the {@link PointsToAnalysis} object. 212 | * @return a reference to the {@link PointsToAnalysis} object 213 | */ 214 | public PointsToAnalysis getPointsToAnalysis() { 215 | return pointsToAnalysis; 216 | } 217 | 218 | 219 | 220 | } 221 | -------------------------------------------------------------------------------- /src/main/java/vasco/callgraph/PointsToAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.callgraph; 19 | 20 | 21 | import java.util.Collections; 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | 25 | import soot.Local; 26 | import soot.RefLikeType; 27 | import soot.RefType; 28 | import soot.Scene; 29 | import soot.SootClass; 30 | import soot.SootField; 31 | import soot.SootMethod; 32 | import soot.Unit; 33 | import soot.Value; 34 | import soot.jimple.AnyNewExpr; 35 | import soot.jimple.ArrayRef; 36 | import soot.jimple.AssignStmt; 37 | import soot.jimple.CastExpr; 38 | import soot.jimple.CaughtExceptionRef; 39 | import soot.jimple.Constant; 40 | import soot.jimple.DefinitionStmt; 41 | import soot.jimple.IdentityRef; 42 | import soot.jimple.InstanceFieldRef; 43 | import soot.jimple.InstanceInvokeExpr; 44 | import soot.jimple.IntConstant; 45 | import soot.jimple.InterfaceInvokeExpr; 46 | import soot.jimple.InvokeExpr; 47 | import soot.jimple.InvokeStmt; 48 | import soot.jimple.NewArrayExpr; 49 | import soot.jimple.ReturnStmt; 50 | import soot.jimple.SpecialInvokeExpr; 51 | import soot.jimple.StaticFieldRef; 52 | import soot.jimple.StaticInvokeExpr; 53 | import soot.jimple.Stmt; 54 | import soot.jimple.VirtualInvokeExpr; 55 | import soot.jimple.internal.JNewArrayExpr; 56 | import vasco.CallSite; 57 | import vasco.Context; 58 | import vasco.OldForwardInterProceduralAnalysis; 59 | import vasco.ProgramRepresentation; 60 | import vasco.soot.DefaultJimpleRepresentation; 61 | 62 | /** 63 | * An inter-procedural analysis for constructing a context-sensitive call graph 64 | * on-the-fly. 65 | * 66 | *

This analysis uses the value-based context-sensitive inter-procedural framework 67 | * and was developed as an example instantiation of the framework.

68 | * 69 | *

The analysis uses {@link PointsToGraph} objects as data flow values, which 70 | * in turn abstracts heap locations using allocation sites.

71 | * 72 | *

Warning! The current implementation of this class uses 73 | * the old API (see {@link OldForwardInterProceduralAnalysis}) without separate 74 | * call/return flow functions. The developer is currently in the process of migrating 75 | * this implementation to the new API (see {@link vasco.ForwardInterProceduralAnalysis ForwardInterProceduralAnalysis}).

76 | * 77 | * @author Rohan Padhye 78 | */ 79 | public class PointsToAnalysis extends OldForwardInterProceduralAnalysis { 80 | 81 | private static final SootMethod DUMMY_METHOD = new SootMethod("DUMMY_METHOD", Collections.EMPTY_LIST, Scene.v().getObjectType()); 82 | 83 | /** 84 | * A shared points-to graph that maintains information about objects 85 | * reachable from static fields (modelled as fields of a dummy global variable). 86 | * 87 | *

For static load/store statements, we union this points-to graph with the 88 | * points-to graph in the flow function, perform the operation, and then 89 | * separate stuff out again.

90 | */ 91 | private PointsToGraph staticHeap; 92 | 93 | /** 94 | * A set of classes whose static initialisers have been processed. 95 | */ 96 | private Set clinitCalled; 97 | 98 | /** 99 | * Constructs a new points-to analysis as a forward-flow inter-procedural 100 | * analysis. 101 | */ 102 | public PointsToAnalysis() { 103 | super(); 104 | 105 | // Play around with these flags 106 | this.freeResultsOnTheFly = true; 107 | this.verbose = true; 108 | 109 | // No classes statically initialised yet 110 | this.clinitCalled = new HashSet(); 111 | 112 | // Create a static points-to graph with a single "global" root object 113 | this.staticHeap = topValue(); 114 | this.staticHeap.assignNew(PointsToGraph.GLOBAL_LOCAL, PointsToGraph.GLOBAL_SITE); 115 | } 116 | 117 | /** 118 | * Returns a points-to graph with the locals of main initialised to 119 | * null, except the command-line arguments which are 120 | * initialised to an array of strings. 121 | */ 122 | @Override 123 | public PointsToGraph boundaryValue(SootMethod entryPoint) { 124 | // For now we only support entry to the main method 125 | assert(entryPoint == Scene.v().getMainMethod()); 126 | 127 | // Ok, start setting up entry value 128 | PointsToGraph entryValue = new PointsToGraph(); 129 | 130 | // Locals of main... (only reference types) 131 | SootMethod mainMethod = Scene.v().getMainMethod(); 132 | for (Local local : mainMethod.getActiveBody().getLocals()) { 133 | if (local.getType() instanceof RefLikeType) { 134 | entryValue.assign(local, null); 135 | } 136 | } 137 | 138 | // Command-line arguments to main... 139 | Local argsLocal = mainMethod.getActiveBody().getParameterLocal(0); 140 | NewArrayExpr argsExpr = new JNewArrayExpr(Scene.v().getRefType("java.lang.String"), IntConstant.v(0)); 141 | entryValue.assignNew(argsLocal, argsExpr); 142 | entryValue.setFieldConstant(argsLocal, PointsToGraph.ARRAY_FIELD, PointsToGraph.STRING_CONST); 143 | 144 | 145 | return entryValue; 146 | } 147 | 148 | /** 149 | * Returns a copy of the given points-to graph. 150 | */ 151 | @Override 152 | public PointsToGraph copy(PointsToGraph graph) { 153 | return new PointsToGraph(graph); 154 | } 155 | 156 | /** 157 | * Performs operations on points-to graphs depending on the statement inside 158 | * a CFG node. 159 | */ 160 | @Override 161 | protected PointsToGraph flowFunction(Context context, Unit unit, PointsToGraph in) 162 | { 163 | 164 | // First set OUT to copy of IN (this is default for most statements). 165 | PointsToGraph out = new PointsToGraph(in); 166 | 167 | // This analysis is written assuming that units are statements (and not, 168 | // for example, basic blocks) 169 | assert (unit instanceof Stmt); 170 | Stmt stmt = (Stmt) unit; 171 | 172 | // What kind of statement? 173 | if (stmt instanceof DefinitionStmt) { 174 | // Assignment of LHS to an RHS 175 | Value lhsOp = ((DefinitionStmt) stmt).getLeftOp(); 176 | Value rhsOp = ((DefinitionStmt) stmt).getRightOp(); 177 | 178 | // Invoke static initialisers if static members accessed 179 | // for the first time 180 | StaticFieldRef staticReference = null; 181 | if (lhsOp instanceof StaticFieldRef) { 182 | staticReference = ((StaticFieldRef) lhsOp); 183 | } else if (rhsOp instanceof StaticFieldRef) { 184 | staticReference = ((StaticFieldRef) rhsOp); 185 | } 186 | if (staticReference != null) { 187 | SootClass declaringClass = staticReference.getField().getDeclaringClass(); 188 | if (clinitCalled.contains(declaringClass) == false) { 189 | clinitCalled.add(declaringClass); 190 | // Don't initialise library classes 191 | if (declaringClass.isLibraryClass()) { 192 | // Set all static fields to null 193 | for (SootField field : declaringClass.getFields()) { 194 | // Only for static reference fields 195 | if (field.isStatic() && field.getType() instanceof RefLikeType) { 196 | staticHeap.setFieldSummary(PointsToGraph.GLOBAL_LOCAL, field); 197 | } 198 | } 199 | } else { 200 | // We have to initialise this class... 201 | if (declaringClass.declaresMethodByName("")) { 202 | // Get the static initialisation method 203 | SootMethod clinit = declaringClass.getMethodByName(""); 204 | // At its entry use a blank value (with STICKY to avoid TOP termination) 205 | PointsToGraph clinitEntryValue = topValue(); 206 | clinitEntryValue.assign(PointsToGraph.STICKY_LOCAL, null); 207 | // Make the call! 208 | this.processCall(context, stmt, clinit,clinitEntryValue); 209 | // Do not process this statement now, wait for clinit to return 210 | // and this statement as a "return site" 211 | return null; 212 | } 213 | // If no defined for this class, then continue as normal :-) 214 | } 215 | } 216 | } 217 | 218 | 219 | // Handle statement depending on type 220 | if (lhsOp.getType() instanceof RefLikeType) { 221 | // Both LHS and RHS are RefLikeType 222 | if (lhsOp instanceof InstanceFieldRef || lhsOp instanceof ArrayRef) { 223 | // SETFIELD 224 | Local lhs = (Local)(lhsOp instanceof InstanceFieldRef ? 225 | ((InstanceFieldRef) lhsOp).getBase() : 226 | ((ArrayRef) lhsOp).getBase()); 227 | SootField field = lhsOp instanceof InstanceFieldRef ? 228 | ((InstanceFieldRef) lhsOp).getField() : PointsToGraph.ARRAY_FIELD; 229 | 230 | // RHS can be a local or constant (string, class, null) 231 | if (rhsOp instanceof Local) { 232 | Local rhs = (Local) rhsOp; 233 | out.setField(lhs, field, rhs); 234 | } else if (rhsOp instanceof Constant) { 235 | Constant rhs = (Constant) rhsOp; 236 | out.setFieldConstant(lhs, field, rhs); 237 | } else { 238 | throw new RuntimeException(rhsOp.toString()); 239 | } 240 | } else if (rhsOp instanceof InstanceFieldRef || rhsOp instanceof ArrayRef) { 241 | // GETFIELD 242 | Local rhs = (Local)(rhsOp instanceof InstanceFieldRef ? 243 | ((InstanceFieldRef) rhsOp).getBase() : 244 | ((ArrayRef) rhsOp).getBase()); 245 | SootField field = rhsOp instanceof InstanceFieldRef ? 246 | ((InstanceFieldRef) rhsOp).getField() : PointsToGraph.ARRAY_FIELD; 247 | 248 | // LHS has to be local 249 | if (lhsOp instanceof Local) { 250 | Local lhs = (Local) lhsOp; 251 | out.getField(lhs, rhs, field); 252 | } else { 253 | throw new RuntimeException(lhsOp.toString()); 254 | } 255 | } else if (rhsOp instanceof AnyNewExpr) { 256 | // NEW, NEWARRAY or NEWMULTIARRAY 257 | AnyNewExpr anyNewExpr = (AnyNewExpr) rhsOp; 258 | if (lhsOp instanceof Local) { 259 | Local lhs = (Local) lhsOp; 260 | out.assignNew(lhs, anyNewExpr); 261 | } else { 262 | throw new RuntimeException(lhsOp.toString()); 263 | } 264 | } else if (rhsOp instanceof InvokeExpr) { 265 | // STATICINVOKE, SPECIALINVOKE, VIRTUALINVOKE or INTERFACEINVOKE 266 | InvokeExpr expr = (InvokeExpr) rhsOp; 267 | // Handle method invocation! 268 | out = handleInvoke(context, stmt, expr, in); 269 | } else if (lhsOp instanceof StaticFieldRef) { 270 | // Get parameters 271 | SootField staticField = ((StaticFieldRef) lhsOp).getField(); 272 | // Temporarily union locals and globals 273 | PointsToGraph tmp = topValue(); 274 | tmp.union(out, staticHeap); 275 | // Store RHS into static field 276 | if (rhsOp instanceof Local) { 277 | Local rhsLocal = (Local) rhsOp; 278 | tmp.setField(PointsToGraph.GLOBAL_LOCAL, staticField, rhsLocal); 279 | } else if (rhsOp instanceof Constant) { 280 | Constant rhsConstant = (Constant) rhsOp; 281 | tmp.setFieldConstant(PointsToGraph.GLOBAL_LOCAL, staticField, rhsConstant); 282 | } else { 283 | throw new RuntimeException(rhsOp.toString()); 284 | } 285 | // Now get rid of all locals, params, etc. 286 | Set locals = new HashSet(tmp.roots.keySet()); 287 | for (Local local : locals) { 288 | // Everything except the GLOBAL must go! 289 | if (local != PointsToGraph.GLOBAL_LOCAL) { 290 | tmp.kill(local); 291 | } 292 | } 293 | // Global information is updated! 294 | staticHeap = tmp; 295 | 296 | } else if (rhsOp instanceof StaticFieldRef) { 297 | // Get parameters 298 | Local lhsLocal = (Local) lhsOp; 299 | SootField staticField = ((StaticFieldRef) rhsOp).getField(); 300 | // Temporarily union locals and globals 301 | PointsToGraph tmp = topValue(); 302 | tmp.union(out, staticHeap); 303 | // Load static field into LHS local 304 | tmp.getField(lhsLocal, PointsToGraph.GLOBAL_LOCAL, staticField); 305 | // Now get rid of globals that we do not care about 306 | tmp.kill(PointsToGraph.GLOBAL_LOCAL); 307 | // Local information is updated! 308 | out = tmp; 309 | 310 | } else if (rhsOp instanceof CaughtExceptionRef) { 311 | Local lhs = (Local) lhsOp; 312 | out.assignSummary(lhs); 313 | } else if (rhsOp instanceof IdentityRef) { 314 | // Ignore identities 315 | } else if (lhsOp instanceof Local) { 316 | // Assignment 317 | Local lhs = (Local) lhsOp; 318 | // RHS op is a local, constant or class cast 319 | if (rhsOp instanceof Local) { 320 | Local rhs = (Local) rhsOp; 321 | out.assign(lhs, rhs); 322 | } else if (rhsOp instanceof Constant) { 323 | Constant rhs = (Constant) rhsOp; 324 | out.assignConstant(lhs, rhs); 325 | } else if (rhsOp instanceof CastExpr) { 326 | Value op = ((CastExpr) rhsOp).getOp(); 327 | if (op instanceof Local) { 328 | Local rhs = (Local) op; 329 | out.assign(lhs, rhs); 330 | } else if (op instanceof Constant) { 331 | Constant rhs = (Constant) op; 332 | out.assignConstant(lhs, rhs); 333 | } else { 334 | throw new RuntimeException(op.toString()); 335 | } 336 | } else { 337 | throw new RuntimeException(rhsOp.toString()); 338 | } 339 | } else { 340 | throw new RuntimeException(unit.toString()); 341 | } 342 | } else if (rhsOp instanceof InvokeExpr) { 343 | // For non-reference types, only method invocations are important 344 | InvokeExpr expr = (InvokeExpr) rhsOp; 345 | // Handle method invocation! 346 | out = handleInvoke(context, stmt, expr, in); 347 | } 348 | 349 | } else if (stmt instanceof InvokeStmt) { 350 | // INVOKE without a return 351 | InvokeExpr expr = stmt.getInvokeExpr(); 352 | // Handle method invocation! 353 | out = handleInvoke(context, stmt, expr, in); 354 | } else if (stmt instanceof ReturnStmt) { 355 | // Returning a value (not return-void as those are of type ReturnVoidStmt) 356 | Value op = ((ReturnStmt) stmt).getOp(); 357 | Local lhs = PointsToGraph.RETURN_LOCAL; 358 | // We only care about reference-type returns 359 | if (op.getType() instanceof RefLikeType) { 360 | // We can return a local or a constant 361 | if (op instanceof Local) { 362 | Local rhs = (Local) op; 363 | out.assign(lhs, rhs); 364 | } else if (op instanceof Constant) { 365 | Constant rhs = (Constant) op; 366 | out.assignConstant(lhs, rhs); 367 | } else { 368 | throw new RuntimeException(op.toString()); 369 | } 370 | } 371 | } 372 | 373 | return out; 374 | 375 | } 376 | 377 | 378 | /** 379 | * Computes the targets of an invoke expression using a given points-to graph. 380 | * 381 | *

For static invocations, there is only target. For instance method 382 | * invocations, the targets depend on the type of receiver objects pointed-to 383 | * by the instance variable whose method is being invoked.

384 | * 385 | *

If the instance variable points to a summary node, then the returned 386 | * value is null signifying a default call-site.

387 | */ 388 | private Set getTargets(SootMethod callerMethod, Stmt callStmt, InvokeExpr ie, PointsToGraph ptg) { 389 | Set targets = new HashSet(); 390 | SootMethod invokedMethod = ie.getMethod(); 391 | String subsignature = invokedMethod.getSubSignature(); 392 | 393 | // Static and special invocations refer to the target method directly 394 | if (ie instanceof StaticInvokeExpr || ie instanceof SpecialInvokeExpr) { 395 | targets.add(invokedMethod); 396 | return targets; 397 | } else { 398 | assert (ie instanceof InterfaceInvokeExpr || ie instanceof VirtualInvokeExpr); 399 | // Get the receiver 400 | Local receiver = (Local) ((InstanceInvokeExpr) ie).getBase(); 401 | // Get what objects the receiver points-to 402 | Set heapNodes = ptg.getTargets(receiver); 403 | if (heapNodes != null) { 404 | // For each object, find the invoked method for the declared type 405 | for (AnyNewExpr heapNode : heapNodes) { 406 | if (heapNode == PointsToGraph.SUMMARY_NODE) { 407 | // If even one pointee is a summary node, then this is a default site 408 | return null; 409 | } else if (heapNode instanceof NewArrayExpr) { 410 | // Probably getClass() or something like that on an array 411 | return null; 412 | } 413 | // Find the top-most class that declares a method with the given 414 | // signature and add it to the resulting targets 415 | SootClass sootClass = ((RefType) heapNode.getType()).getSootClass(); 416 | do { 417 | if (sootClass.declaresMethod(subsignature)) { 418 | targets.add(sootClass.getMethod(subsignature)); 419 | break; 420 | } else if (sootClass.hasSuperclass()) { 421 | sootClass = sootClass.getSuperclass(); 422 | } else { 423 | sootClass = null; 424 | } 425 | } while (sootClass != null); 426 | } 427 | } 428 | if (targets.isEmpty()) { 429 | // System.err.println("Warning! Null call at: " + callStmt+ " in " + callerMethod); 430 | } 431 | return targets; 432 | } 433 | } 434 | 435 | 436 | private Set getDummyTarget() { 437 | Set targets = new HashSet(); 438 | targets.add(DUMMY_METHOD); 439 | return targets; 440 | } 441 | 442 | /** 443 | * Handles a call site by resolving the targets of the method invocation. 444 | * 445 | * The resultant flow is the union of the exit flows of all the analysed 446 | * callees. If the method returns a reference-like value, this is also taken 447 | * into account. 448 | */ 449 | protected PointsToGraph handleInvoke(Context callerContext, Stmt callStmt, 450 | InvokeExpr ie, PointsToGraph in) { 451 | // Get the caller method 452 | SootMethod callerMethod = callerContext.getMethod(); 453 | // Initialise the final result as TOP first 454 | PointsToGraph resultFlow = topValue(); 455 | 456 | // If this statement is an assignment to an object, then set LHS for 457 | // RETURN values. 458 | Local lhs = null; 459 | Value lhsOp = null; 460 | if (callStmt instanceof AssignStmt) { 461 | lhsOp = ((AssignStmt) callStmt).getLeftOp(); 462 | if (lhsOp.getType() instanceof RefLikeType) { 463 | lhs = (Local) lhsOp; 464 | } 465 | } 466 | 467 | // Find target methods for this call site (invoke expression) using the points-to data 468 | Set targets = getTargets(callerMethod, callStmt, ie, in); 469 | 470 | // If "targets" is null, that means the invoking instance was SUMMARY 471 | // So we use the DUMMY METHOD (which is a method with no body) 472 | if (targets == null) { 473 | targets = getDummyTarget(); 474 | this.contextTransitions.addTransition(new CallSite(callerContext, callStmt), null); 475 | if (verbose) { 476 | System.out.println("[DEF] X" + callerContext + " -> DEFAULT " + ie.getMethod()); 477 | } 478 | } 479 | 480 | // Make calls for all target methods 481 | for (SootMethod calledMethod : targets) { 482 | 483 | // The call-edge is obtained by assign parameters and THIS, and killing caller's locals 484 | PointsToGraph callEdge = copy(in); 485 | if (calledMethod.hasActiveBody()) { 486 | // We need to maintain a set of locals not to kill (in case the call is recursive) 487 | Set doNotKill = new HashSet(); 488 | 489 | // Initialise sticky parameter 490 | callEdge.assign(PointsToGraph.STICKY_LOCAL, null); 491 | doNotKill.add(PointsToGraph.STICKY_LOCAL); 492 | 493 | // Assign this... 494 | if (ie instanceof InstanceInvokeExpr) { 495 | Local receiver = (Local)((InstanceInvokeExpr) ie).getBase(); 496 | Local thisLocal = calledMethod.getActiveBody().getThisLocal(); 497 | callEdge.assign(thisLocal, receiver); 498 | doNotKill.add(thisLocal); 499 | // Sticky it! 500 | callEdge.assignSticky(PointsToGraph.STICKY_LOCAL, thisLocal); 501 | } 502 | 503 | // Assign parameters... 504 | for (int i = 0; i < calledMethod.getParameterCount(); i++) { 505 | // Only for reference-like parameters 506 | if (calledMethod.getParameterType(i) instanceof RefLikeType) { 507 | Local parameter = calledMethod.getActiveBody().getParameterLocal(i); 508 | Value argValue = ie.getArg(i); 509 | // The argument can be a constant or local, so handle accordingly 510 | if (argValue instanceof Local) { 511 | Local argLocal = (Local) argValue; 512 | callEdge.assign(parameter, argLocal); 513 | doNotKill.add(parameter); 514 | // Sticky it! 515 | callEdge.assignSticky(PointsToGraph.STICKY_LOCAL, argLocal); 516 | } else if (argValue instanceof Constant) { 517 | Constant argConstant = (Constant) argValue; 518 | callEdge.assignConstant(parameter, argConstant); 519 | doNotKill.add(parameter); 520 | // No need to sticky constants as caller does not store them anyway 521 | } else { 522 | throw new RuntimeException(argValue.toString()); 523 | } 524 | } 525 | } 526 | 527 | // Kill caller data... 528 | for (Local callerLocal : callerMethod.getActiveBody().getLocals()) { 529 | if (doNotKill.contains(callerLocal) == false) 530 | callEdge.kill(callerLocal); 531 | } 532 | 533 | // There should be no "return local", but we kill it anyway (just in case) 534 | callEdge.kill(PointsToGraph.RETURN_LOCAL); 535 | 536 | // Create callee locals.. 537 | for (Local calleeLocal : calledMethod.getActiveBody().getLocals()) { 538 | if (calleeLocal.getType() instanceof RefLikeType 539 | && doNotKill.contains(calleeLocal) == false) { 540 | callEdge.assign(calleeLocal, null); 541 | } 542 | } 543 | } 544 | 545 | // The intra-procedural edge is the IN value minus the objects from the call edge 546 | PointsToGraph intraEdge = copy(in); 547 | if (lhs != null) { 548 | // Oh, and remove the LHS targets too 549 | intraEdge.assign(lhs, null); 550 | } 551 | //intraEdge.subtractHeap(callEdge); 552 | 553 | // Value at the start of the called procedure is 554 | // whatever went through the call edge 555 | PointsToGraph entryFlow = callEdge; 556 | 557 | 558 | 559 | // Make the call to this method!! (in case of body-less methods, no change) 560 | PointsToGraph exitFlow = calledMethod.hasActiveBody() ? 561 | processCall(callerContext, callStmt, calledMethod, entryFlow) : entryFlow; 562 | 563 | // If the called context was analysed, exitFlow will be set, else it 564 | // will be null. 565 | if (exitFlow != null) { 566 | 567 | // Propagate stuff from called procedure's exit to the caller's return-site 568 | PointsToGraph returnEdge = copy(exitFlow); 569 | 570 | // Two ways to handle this: 571 | if (calledMethod.hasActiveBody()) { 572 | // Kill all the called method's locals. That's right. 573 | for (Local calleeLocal : calledMethod.getActiveBody().getLocals()) { 574 | returnEdge.kill(calleeLocal); 575 | } 576 | // Remove the stickies (so not to interfere with stickies in the intra-edge) 577 | // but do not collect unreachable nodes 578 | returnEdge.killWithoutGC(PointsToGraph.STICKY_LOCAL); 579 | 580 | } 581 | 582 | // Let's unite the intra-edge with the return edge 583 | PointsToGraph callOut = topValue(); 584 | callOut.union(intraEdge, returnEdge); 585 | 586 | 587 | // Now we are only left with the return value, if any 588 | if (calledMethod.hasActiveBody()) { 589 | if (lhs != null) { 590 | callOut.assign(lhs, PointsToGraph.RETURN_LOCAL); 591 | } 592 | // Kill the @return variable whether there was an LHS or not 593 | callOut.kill(PointsToGraph.RETURN_LOCAL); 594 | } else { 595 | // Handle returned objects for native methods 596 | if (lhs != null) { 597 | // If a string is returned, optimise 598 | if (lhs.getType().equals(PointsToGraph.STRING_CONST.getType())) { 599 | callOut.assignConstant(lhs, PointsToGraph.STRING_CONST); 600 | } else if (lhs.getType().equals(PointsToGraph.CLASS_CONST.getType())) { 601 | callOut.assignConstant(lhs, PointsToGraph.CLASS_CONST); 602 | } else { 603 | // Have to assume the worst! 604 | //System.err.println("Warning! Summary node returned at " + 605 | // callStmt + " in " + callerMethod); 606 | callOut.assignSummary(lhs); 607 | } 608 | } 609 | // Also assume that all parameters are modified 610 | for (int i = 0; i < calledMethod.getParameterCount(); i++) { 611 | // Only for reference-like parameters 612 | if (calledMethod.getParameterType(i) instanceof RefLikeType) { 613 | Value argValue = ie.getArg(i); 614 | // Summarize if the argument is local (i.e. not a constant) 615 | if (argValue instanceof Local) { 616 | Local argLocal = (Local) argValue; 617 | callOut.summarizeTargetFields(argLocal); 618 | } 619 | } 620 | } 621 | } 622 | 623 | // As we may have multiple virtual calls, merge the value at OUT 624 | // of this target's call-site with an accumulator (resultFlow) 625 | resultFlow = meet(resultFlow, callOut); 626 | } 627 | } 628 | 629 | // If at least one call succeeded, result flow is not TOP 630 | if (resultFlow.equals(topValue())) { 631 | return null; 632 | } else { 633 | return resultFlow; 634 | } 635 | } 636 | 637 | 638 | 639 | 640 | 641 | /** 642 | * Returns the union of two points-to graphs. 643 | */ 644 | @Override 645 | public PointsToGraph meet(PointsToGraph op1, PointsToGraph op2) { 646 | PointsToGraph result = new PointsToGraph(); 647 | result.union(op1, op2); 648 | return result; 649 | } 650 | 651 | /** 652 | * The default data flow value (lattice top) is the empty points-to graph. 653 | */ 654 | @Override 655 | public PointsToGraph topValue() { 656 | return new PointsToGraph(); 657 | } 658 | 659 | @Override 660 | public ProgramRepresentation programRepresentation() { 661 | return DefaultJimpleRepresentation.v(); 662 | } 663 | } 664 | -------------------------------------------------------------------------------- /src/main/java/vasco/soot/ContextSensitiveJimpleRepresentation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.soot; 19 | 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.Iterator; 23 | import java.util.LinkedList; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | import soot.MethodContext; 28 | import soot.MethodOrMethodContext; 29 | import soot.Scene; 30 | import soot.SootMethod; 31 | import soot.Unit; 32 | import soot.jimple.toolkits.callgraph.ContextSensitiveEdge; 33 | import soot.toolkits.graph.DirectedGraph; 34 | import soot.toolkits.graph.ExceptionalUnitGraph; 35 | import vasco.ProgramRepresentation; 36 | 37 | /** 38 | * A program representation for Soot using the Jimple IR with a context-sensitive 39 | * call graph. This representation uses control-flow graphs of individual units including exceptional 40 | * control flow, and resolves virtual calls using the call graph returned by 41 | * {@link soot.Scene#getContextSensitiveCallGraph() Scene#getContextSensitiveCallGraph}. 42 | * 43 | *

Note: This class follows the Singleton pattern. The singleton 44 | * object is available through {@link #v()}.

45 | * 46 | * @author Rohan Padhye 47 | * 48 | */ 49 | public class ContextSensitiveJimpleRepresentation implements ProgramRepresentation { 50 | 51 | // Cache for control flow graphs 52 | private Map> cfgCache; 53 | 54 | // Private constructor, see #v() to retrieve singleton object 55 | private ContextSensitiveJimpleRepresentation() { 56 | cfgCache = new HashMap>(); 57 | } 58 | 59 | /** 60 | * Returns a singleton list containing the main method. 61 | * @see Scene#getMainMethod() 62 | */ 63 | @Override 64 | public List getEntryPoints() { 65 | return Collections.singletonList(MethodContext.v(Scene.v().getMainMethod(), null)); 66 | } 67 | 68 | /** 69 | * Returns an {@link ExceptionalUnitGraph} for a given method. 70 | */ 71 | @Override 72 | public DirectedGraph getControlFlowGraph(MethodOrMethodContext momc) { 73 | if (cfgCache.containsKey(momc.method()) == false) { 74 | cfgCache.put(momc.method(), new ExceptionalUnitGraph(momc.method().getActiveBody())); 75 | } 76 | return cfgCache.get(momc.method()); 77 | } 78 | 79 | /** 80 | * Returns true iff the Jimple statement contains an 81 | * invoke expression. 82 | */ 83 | @Override 84 | public boolean isCall(Unit node) { 85 | return ((soot.jimple.Stmt) node).containsInvokeExpr(); 86 | } 87 | 88 | /** 89 | * Resolves virtual calls using the Soot's context-sensitive call graph and returns 90 | * a list of method-contexts which are the targets of explicit edges. 91 | */ 92 | @Override 93 | public List resolveTargets(MethodOrMethodContext momc, Unit node) { 94 | List targets = new LinkedList(); 95 | @SuppressWarnings("rawtypes") 96 | Iterator it = Scene.v().getContextSensitiveCallGraph().edgesOutOf(momc.context(), momc.method(), node); 97 | while(it.hasNext()) { 98 | ContextSensitiveEdge edge = (ContextSensitiveEdge) it.next(); 99 | if (edge.kind().isExplicit()) { 100 | targets.add(MethodContext.v(edge.tgt(), edge.tgtCtxt())); 101 | } 102 | } 103 | return targets; 104 | } 105 | 106 | // The singleton object 107 | private static ContextSensitiveJimpleRepresentation singleton = new ContextSensitiveJimpleRepresentation(); 108 | 109 | /** 110 | * Returns a reference to the singleton object of this class. 111 | */ 112 | public static ContextSensitiveJimpleRepresentation v() { return singleton; } 113 | 114 | @Override 115 | public boolean isPhantomMethod(MethodOrMethodContext method) { 116 | // TODO Auto-generated method stub 117 | return false; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/vasco/soot/DefaultJimpleRepresentation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.soot; 19 | 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.Iterator; 23 | import java.util.LinkedList; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | import soot.Scene; 28 | import soot.SootMethod; 29 | import soot.Unit; 30 | import soot.jimple.toolkits.callgraph.Edge; 31 | import soot.toolkits.graph.DirectedGraph; 32 | import soot.toolkits.graph.ExceptionalUnitGraph; 33 | import vasco.ProgramRepresentation; 34 | 35 | /** 36 | * A default program representation for Soot using the Jimple IR. This 37 | * representation uses control-flow graphs of individual units including exceptional 38 | * control flow, and resolves virtual calls using the default context-insensitive 39 | * call graph. 40 | * 41 | *

Note: This class follows the Singleton pattern. The singleton 42 | * object is available through {@link #v()}.

43 | * 44 | * @author Rohan Padhye 45 | * 46 | */ 47 | public class DefaultJimpleRepresentation implements ProgramRepresentation { 48 | 49 | // Cache for control flow graphs 50 | private Map> cfgCache; 51 | 52 | // Private constructor, see #v() to retrieve singleton object 53 | private DefaultJimpleRepresentation() { 54 | cfgCache = new HashMap>(); 55 | } 56 | 57 | /** 58 | * Returns a singleton list containing the main method. 59 | * @see Scene#getMainMethod() 60 | */ 61 | @Override 62 | public List getEntryPoints() { 63 | return Collections.singletonList(Scene.v().getMainMethod()); 64 | } 65 | 66 | /** 67 | * Returns an {@link ExceptionalUnitGraph} for a given method. 68 | */ 69 | @Override 70 | public DirectedGraph getControlFlowGraph(SootMethod method) { 71 | if (cfgCache.containsKey(method) == false) { 72 | cfgCache.put(method, new ExceptionalUnitGraph(method.getActiveBody())); 73 | } 74 | return cfgCache.get(method); 75 | } 76 | 77 | /** 78 | * Returns true iff the Jimple statement contains an 79 | * invoke expression. 80 | */ 81 | @Override 82 | public boolean isCall(Unit node) { 83 | return ((soot.jimple.Stmt) node).containsInvokeExpr(); 84 | } 85 | 86 | /** 87 | * Resolves virtual calls using the default call graph and returns 88 | * a list of methods which are the targets of explicit edges. 89 | * TODO: Should we consider thread/clinit edges? 90 | */ 91 | @Override 92 | public List resolveTargets(SootMethod method, Unit node) { 93 | List targets = new LinkedList(); 94 | Iterator it = Scene.v().getCallGraph().edgesOutOf(node); 95 | while(it.hasNext()) { 96 | Edge edge = it.next(); 97 | if (edge.isExplicit()) { 98 | targets.add(edge.tgt()); 99 | } 100 | } 101 | return targets; 102 | } 103 | 104 | // The singleton object 105 | private static DefaultJimpleRepresentation singleton = new DefaultJimpleRepresentation(); 106 | 107 | /** 108 | * Returns a reference to the singleton object of this class. 109 | */ 110 | public static DefaultJimpleRepresentation v() { return singleton; } 111 | 112 | @Override 113 | public boolean isPhantomMethod(SootMethod method) { 114 | return method.isPhantom() || !method.hasActiveBody(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/vasco/soot/examples/CopyConstantAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.soot.examples; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import soot.Local; 24 | import soot.SootMethod; 25 | import soot.Unit; 26 | import soot.Value; 27 | import soot.jimple.AssignStmt; 28 | import soot.jimple.CastExpr; 29 | import soot.jimple.Constant; 30 | import soot.jimple.InstanceInvokeExpr; 31 | import soot.jimple.InvokeExpr; 32 | import soot.jimple.ReturnStmt; 33 | import soot.jimple.Stmt; 34 | import soot.jimple.internal.JimpleLocal; 35 | import vasco.Context; 36 | import vasco.ForwardInterProceduralAnalysis; 37 | import vasco.ProgramRepresentation; 38 | import vasco.soot.DefaultJimpleRepresentation; 39 | 40 | /** 41 | * An inter-procedural copy constant propagation analysis. 42 | * 43 | *

This analysis uses a mapping of {@link Local}s to {@link Constant}s as 44 | * data flow values. The flow functions consider assignments of constants 45 | * to locals (immediate operands) as well as assignments of locals to locals 46 | * where the operand has a constant value. This type of analysis is commonly referred 47 | * to as copy constant propagation.

48 | * 49 | * 50 | * @author Rohan Padhye 51 | * 52 | */ 53 | public class CopyConstantAnalysis extends ForwardInterProceduralAnalysis> { 54 | 55 | // An artificial local representing returned value of a procedure (used because a method can have multiple return statements). 56 | private static final Local RETURN_LOCAL = new JimpleLocal("@return", null); 57 | 58 | // Simply constructs a forward flow inter-procedural analysis with the VERBOSE option set. 59 | public CopyConstantAnalysis() { 60 | super(); 61 | verbose = true; 62 | } 63 | 64 | // Private utility method to assign the constant value of the RHS (if any) from the input map to the LHS in the output map. 65 | private void assign(Local lhs, Value rhs, Map input, Map output) { 66 | // First remove casts, if any. 67 | if (rhs instanceof CastExpr) { 68 | rhs = ((CastExpr) rhs).getOp(); 69 | } 70 | // Then check if the RHS operand is a constant or local 71 | if (rhs instanceof Constant) { 72 | // If RHS is a constant, it is a direct gen 73 | output.put(lhs, (Constant) rhs); 74 | } else if (rhs instanceof Local) { 75 | // Copy constant-status of RHS to LHS (indirect gen), if exists 76 | if(input.containsKey(rhs)) { 77 | output.put(lhs, input.get(rhs)); 78 | } 79 | } else { 80 | // RHS is some compound expression, then LHS is non-constant (only kill) 81 | output.put(lhs, null); 82 | } 83 | } 84 | 85 | @Override 86 | public Map normalFlowFunction(Context> context, Unit unit, 87 | Map inValue) { 88 | // Initialize result to input 89 | Map outValue = copy(inValue); 90 | // Only statements assigning locals matter 91 | if (unit instanceof AssignStmt) { 92 | // Get operands 93 | Value lhsOp = ((AssignStmt) unit).getLeftOp(); 94 | Value rhsOp = ((AssignStmt) unit).getRightOp(); 95 | if (lhsOp instanceof Local) { 96 | assign((Local) lhsOp, rhsOp, inValue, outValue); 97 | } 98 | } else if (unit instanceof ReturnStmt) { 99 | // Get operand 100 | Value rhsOp = ((ReturnStmt) unit).getOp(); 101 | assign(RETURN_LOCAL, rhsOp, inValue, outValue); 102 | } 103 | // Return the data flow value at the OUT of the statement 104 | return outValue; 105 | } 106 | 107 | @Override 108 | public Map callEntryFlowFunction(Context> context, SootMethod calledMethod, Unit unit, Map inValue) { 109 | // Initialise result to empty map 110 | Map entryValue = topValue(); 111 | // Map arguments to parameters 112 | InvokeExpr ie = ((Stmt) unit).getInvokeExpr(); 113 | for (int i = 0; i < ie.getArgCount(); i++) { 114 | Value arg = ie.getArg(i); 115 | Local param = calledMethod.getActiveBody().getParameterLocal(i); 116 | assign(param, arg, inValue, entryValue); 117 | } 118 | // And instance of the this local 119 | if (ie instanceof InstanceInvokeExpr) { 120 | Value instance = ((InstanceInvokeExpr) ie).getBase(); 121 | Local thisLocal = calledMethod.getActiveBody().getThisLocal(); 122 | assign(thisLocal, instance, inValue, entryValue); 123 | } 124 | // Return the entry value at the called method 125 | return entryValue; 126 | } 127 | 128 | @Override 129 | public Map callExitFlowFunction(Context> context, SootMethod calledMethod, Unit unit, Map exitValue) { 130 | // Initialise result to an empty value 131 | Map afterCallValue = topValue(); 132 | // Only propagate constants for return values 133 | if (unit instanceof AssignStmt) { 134 | Value lhsOp = ((AssignStmt) unit).getLeftOp(); 135 | assign((Local) lhsOp, RETURN_LOCAL, exitValue, afterCallValue); 136 | } 137 | // Return the map with the returned value's constant 138 | return afterCallValue; 139 | } 140 | 141 | @Override 142 | public Map callLocalFlowFunction(Context> context, Unit unit, Map inValue) { 143 | // Initialise result to the input 144 | Map afterCallValue = copy(inValue); 145 | // Remove information for return value (as it's value will flow from the call) 146 | if (unit instanceof AssignStmt) { 147 | Value lhsOp = ((AssignStmt) unit).getLeftOp(); 148 | afterCallValue.remove(lhsOp); 149 | } 150 | // Rest of the map remains the same 151 | return afterCallValue; 152 | 153 | } 154 | 155 | @Override 156 | public Map boundaryValue(SootMethod method) { 157 | return topValue(); 158 | } 159 | 160 | @Override 161 | public Map copy(Map src) { 162 | return new HashMap(src); 163 | } 164 | 165 | 166 | 167 | @Override 168 | public Map meet(Map op1, Map op2) { 169 | Map result; 170 | // First add everything in the first operand 171 | result = new HashMap(op1); 172 | // Then add everything in the second operand, bottoming out the common keys with different values 173 | for (Local x : op2.keySet()) { 174 | if (op1.containsKey(x)) { 175 | // Check the values in both operands 176 | Constant c1 = op1.get(x); 177 | Constant c2 = op2.get(x); 178 | if (c1 != null && c1.equals(c2) == false) { 179 | // Set to non-constant 180 | result.put(x, null); 181 | } 182 | } else { 183 | // Only in second operand, so add as-is 184 | result.put(x, op2.get(x)); 185 | } 186 | } 187 | return result; 188 | } 189 | 190 | /** 191 | * Returns an empty map. 192 | */ 193 | @Override 194 | public Map topValue() { 195 | return new HashMap(); 196 | } 197 | 198 | /** 199 | * Returns a default jimple representation. 200 | * @see DefaultJimpleRepresentation 201 | */ 202 | @Override 203 | public ProgramRepresentation programRepresentation() { 204 | return DefaultJimpleRepresentation.v(); 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/vasco/soot/examples/SignAnalysis.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.soot.examples; 19 | 20 | import static vasco.soot.examples.SignAnalysis.Sign.BOTTOM; 21 | import static vasco.soot.examples.SignAnalysis.Sign.NEGATIVE; 22 | import static vasco.soot.examples.SignAnalysis.Sign.POSITIVE; 23 | import static vasco.soot.examples.SignAnalysis.Sign.ZERO; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | import soot.IntType; 29 | import soot.Local; 30 | import soot.SootMethod; 31 | import soot.Unit; 32 | import soot.Value; 33 | import soot.jimple.AddExpr; 34 | import soot.jimple.AssignStmt; 35 | import soot.jimple.BinopExpr; 36 | import soot.jimple.CastExpr; 37 | import soot.jimple.InstanceInvokeExpr; 38 | import soot.jimple.IntConstant; 39 | import soot.jimple.InvokeExpr; 40 | import soot.jimple.MulExpr; 41 | import soot.jimple.NumericConstant; 42 | import soot.jimple.ReturnStmt; 43 | import soot.jimple.Stmt; 44 | import soot.jimple.UnopExpr; 45 | import soot.jimple.internal.AbstractNegExpr; 46 | import soot.jimple.internal.JimpleLocal; 47 | import vasco.Context; 48 | import vasco.ForwardInterProceduralAnalysis; 49 | import vasco.ProgramRepresentation; 50 | import vasco.soot.DefaultJimpleRepresentation; 51 | 52 | /** 53 | * An inter-procedural simplified sign analysis. 54 | * 55 | *

This analysis maps numeric variables to a sign (negative, positive or 56 | * zero), if it is statically determined to be singular, or else BOTTOM. 57 | * 58 | *

Flow functions are non-distributive for statements involving sums or 59 | * products of two variables.

60 | * 61 | *

This is an example implementation only and hence only support analysis 62 | * of integer-valued local variables, and only handles addition, multiplication 63 | * and unary negation of such variables or values.

64 | * 65 | * @author Rohan Padhye 66 | * 67 | */ 68 | public class SignAnalysis extends ForwardInterProceduralAnalysis> { 69 | 70 | 71 | // An artificial local representing returned value of a procedure (used because a method can have multiple return statements). 72 | private static final Local RETURN_LOCAL = new JimpleLocal("@return", IntType.v()); 73 | 74 | // Simply constructs a forward flow inter-procedural analysis with the VERBOSE option set. 75 | public SignAnalysis() { 76 | super(); 77 | verbose = true; 78 | } 79 | 80 | // Returns the sign of a constant or local if it is determinable, or else BOTTOM if the sign cannot be determined. */ 81 | private Sign signOf(Value value, Map dfv) { 82 | if (value instanceof Local) { 83 | // if the value is a local variable, then look-up the data-flow map 84 | Local local = (Local) value; 85 | if (dfv.containsKey(local)) { 86 | return dfv.get(local); 87 | } else { 88 | return Sign.TOP; 89 | } 90 | } else if (value instanceof NumericConstant) { 91 | // If the value is a constant, we can get a definite sign 92 | NumericConstant num = (NumericConstant) value; 93 | NumericConstant zero = IntConstant.v(0); 94 | NumericConstant one = IntConstant.v(1); 95 | if (num.lessThan(zero).equals(one)) { 96 | return NEGATIVE; 97 | } else if (num.greaterThan(zero).equals(one)) { 98 | return POSITIVE; 99 | } else { 100 | return ZERO; 101 | } 102 | } else if (value instanceof BinopExpr) { 103 | BinopExpr expr = (BinopExpr) value; 104 | Value op1 = expr.getOp1(); 105 | Value op2 = expr.getOp2(); 106 | Sign sign1 = signOf(op1, dfv); 107 | Sign sign2 = signOf(op2, dfv); 108 | if (value instanceof AddExpr) { 109 | // Handle arithmetic plus 110 | return sign1.plus(sign2); 111 | } else if (value instanceof MulExpr) { 112 | // Handle arithmetic multiplication 113 | return sign1.mult(sign2); 114 | } else { 115 | // We do not handle other types of binary expressions 116 | return BOTTOM; 117 | } 118 | } else if (value instanceof UnopExpr) { 119 | if (value instanceof AbstractNegExpr) { 120 | // Handle unary minus 121 | Value op = ((AbstractNegExpr) value).getOp(); 122 | Sign sign = signOf(op, dfv); 123 | return sign.negate(); 124 | } else { 125 | // We do not handle other types of binary expressions 126 | return BOTTOM; 127 | } 128 | } else { 129 | // We do not handle other types of compound expressions 130 | return BOTTOM; 131 | } 132 | } 133 | 134 | // Private utility method to assign the constant value of the RHS (if any) from the input map to the LHS in the output map. 135 | private void assign(Local lhs, Value rhs, Map input, Map output) { 136 | // We only care about numeric locals 137 | if (lhs.getType() instanceof IntType) { 138 | // First remove casts, if any. 139 | if (rhs instanceof CastExpr) { 140 | rhs = ((CastExpr) rhs).getOp(); 141 | } 142 | // Determine the sign of the RHS and add it to the map 143 | Sign sign = signOf(rhs, input); 144 | output.put(lhs, sign); 145 | } 146 | } 147 | 148 | @Override 149 | public Map normalFlowFunction( 150 | Context> context, Unit unit, 151 | Map inValue) { 152 | // Initialize result to input 153 | Map outValue = copy(inValue); 154 | // Only statements assigning locals matter 155 | if (unit instanceof AssignStmt) { 156 | // Get operands 157 | Value lhsOp = ((AssignStmt) unit).getLeftOp(); 158 | Value rhsOp = ((AssignStmt) unit).getRightOp(); 159 | if (lhsOp instanceof Local) { 160 | assign((Local) lhsOp, rhsOp, inValue, outValue); 161 | } 162 | } else if (unit instanceof ReturnStmt) { 163 | // Get operand 164 | Value rhsOp = ((ReturnStmt) unit).getOp(); 165 | assign(RETURN_LOCAL, rhsOp, inValue, outValue); 166 | } 167 | // Return the data flow value at the OUT of the statement 168 | return outValue; 169 | } 170 | 171 | @Override 172 | public Map callEntryFlowFunction( 173 | Context> context, SootMethod calledMethod, Unit unit, 174 | Map inValue) { 175 | // Initialise result to empty map 176 | Map entryValue = topValue(); 177 | // Map arguments to parameters 178 | InvokeExpr ie = ((Stmt) unit).getInvokeExpr(); 179 | for (int i = 0; i < ie.getArgCount(); i++) { 180 | Value arg = ie.getArg(i); 181 | Local param = calledMethod.getActiveBody().getParameterLocal(i); 182 | assign(param, arg, inValue, entryValue); 183 | } 184 | // And instance of the this local 185 | if (ie instanceof InstanceInvokeExpr) { 186 | Value instance = ((InstanceInvokeExpr) ie).getBase(); 187 | Local thisLocal = calledMethod.getActiveBody().getThisLocal(); 188 | assign(thisLocal, instance, inValue, entryValue); 189 | } 190 | // Return the entry value at the called method 191 | return entryValue; 192 | } 193 | 194 | @Override 195 | public Map callExitFlowFunction(Context> context, SootMethod calledMethod, Unit unit, Map exitValue) { 196 | // Initialise result to an empty value 197 | Map afterCallValue = topValue(); 198 | // Only propagate signs for return values 199 | if (unit instanceof AssignStmt) { 200 | Value lhsOp = ((AssignStmt) unit).getLeftOp(); 201 | assign((Local) lhsOp, RETURN_LOCAL, exitValue, afterCallValue); 202 | } 203 | // Return the map with the returned value's sign 204 | return afterCallValue; 205 | } 206 | 207 | @Override 208 | public Map callLocalFlowFunction(Context> context, Unit unit, Map inValue) { 209 | // Initialise result to the input 210 | Map afterCallValue = copy(inValue); 211 | // Remove information for return value (as it's value will flow from the call) 212 | if (unit instanceof AssignStmt) { 213 | Value lhsOp = ((AssignStmt) unit).getLeftOp(); 214 | afterCallValue.remove(lhsOp); 215 | } 216 | // Rest of the map remains the same 217 | return afterCallValue; 218 | 219 | } 220 | 221 | @Override 222 | public Map boundaryValue(SootMethod method) { 223 | return topValue(); 224 | } 225 | 226 | @Override 227 | public Map copy(Map src) { 228 | return new HashMap(src); 229 | } 230 | 231 | 232 | 233 | @Override 234 | public Map meet(Map op1, Map op2) { 235 | Map result; 236 | // First add everything in the first operand 237 | result = new HashMap(op1); 238 | // Then add everything in the second operand, bottoming out the common keys with different values 239 | for (Local x : op2.keySet()) { 240 | if (op1.containsKey(x)) { 241 | // Check the values in both operands 242 | Sign sign1 = op1.get(x); 243 | Sign sign2 = op2.get(x); 244 | result.put(x, sign1.meet(sign2)); 245 | } else { 246 | // Only in second operand, so add as-is 247 | result.put(x, op2.get(x)); 248 | } 249 | } 250 | return result; 251 | } 252 | 253 | /** 254 | * Returns an empty map. 255 | */ 256 | @Override 257 | public Map topValue() { 258 | return new HashMap(); 259 | } 260 | 261 | /** 262 | * Returns a default jimple representation. 263 | * @see DefaultJimpleRepresentation 264 | */ 265 | @Override 266 | public ProgramRepresentation programRepresentation() { 267 | return DefaultJimpleRepresentation.v(); 268 | } 269 | 270 | /** A data-flow value representation of a sign. */ 271 | public static interface Sign { 272 | 273 | /** Returns a sign which is the lattice MEET of this sign with the argument. */ 274 | public Sign meet(Sign other); 275 | 276 | /** Returns a sign which is the result of adding a number with this sign with a number having the sign of the argument. */ 277 | public Sign plus(Sign other); 278 | 279 | /** Returns a sign which is the result of multiplying a number with this sign with a number having the sign of the argument. */ 280 | public Sign mult(Sign other); 281 | 282 | /** Returns a sign which is the result of negating a number with this sign. */ 283 | public Sign negate(); 284 | 285 | /** An unknown sign representing the BOTTOM value of the lattice. */ 286 | public static final Sign BOTTOM = new Sign() { 287 | 288 | @Override 289 | public Sign meet(Sign other) { 290 | return BOTTOM; 291 | } 292 | 293 | @Override 294 | public Sign plus(Sign other) { 295 | return BOTTOM; 296 | } 297 | 298 | @Override 299 | public Sign mult(Sign other) { 300 | return BOTTOM; 301 | } 302 | 303 | @Override 304 | public Sign negate() { 305 | return BOTTOM; 306 | } 307 | 308 | @Override 309 | public String toString() { 310 | return "_|_"; 311 | } 312 | }; 313 | 314 | /** The sign of an undefined variable, representing the TOP value of the lattice. */ 315 | public static final Sign TOP = new Sign() { 316 | 317 | @Override 318 | public Sign meet(Sign other) { 319 | return other; 320 | } 321 | 322 | @Override 323 | public Sign plus(Sign other) { 324 | return other; 325 | } 326 | 327 | @Override 328 | public Sign mult(Sign other) { 329 | return other; 330 | } 331 | 332 | @Override 333 | public Sign negate() { 334 | return TOP; 335 | } 336 | 337 | @Override 338 | public String toString() { 339 | return "T"; 340 | } 341 | }; 342 | 343 | /** The sign of the number 0. */ 344 | public static final Sign ZERO = new Sign() { 345 | 346 | @Override 347 | public Sign meet(Sign other) { 348 | if (other == TOP || other == ZERO) { 349 | return ZERO; 350 | } else { 351 | return BOTTOM; 352 | } 353 | } 354 | 355 | @Override 356 | public Sign plus(Sign other) { 357 | if (other == TOP || other == ZERO) { 358 | return ZERO; 359 | } else if (other == POSITIVE) { 360 | return POSITIVE; 361 | } else if (other == NEGATIVE) { 362 | return NEGATIVE; 363 | } else { 364 | return BOTTOM; 365 | } 366 | } 367 | 368 | @Override 369 | public Sign mult(Sign other) { 370 | if (other == TOP || other == ZERO || other == POSITIVE || other == NEGATIVE) { 371 | return ZERO; 372 | } else { 373 | return BOTTOM; 374 | } 375 | } 376 | 377 | @Override 378 | public Sign negate() { 379 | return ZERO; 380 | } 381 | 382 | @Override 383 | public String toString() { 384 | return "0"; 385 | } 386 | }; 387 | 388 | /** The sign of positive numbers. */ 389 | public static final Sign POSITIVE = new Sign() { 390 | 391 | @Override 392 | public Sign meet(Sign other) { 393 | if (other == TOP || other == POSITIVE) { 394 | return POSITIVE; 395 | } else { 396 | return BOTTOM; 397 | } 398 | } 399 | 400 | @Override 401 | public Sign plus(Sign other) { 402 | if (other == TOP || other == POSITIVE || other == ZERO) { 403 | return POSITIVE; 404 | } else if (other == NEGATIVE) { 405 | return BOTTOM; 406 | } else { 407 | return BOTTOM; 408 | } 409 | } 410 | 411 | @Override 412 | public Sign mult(Sign other) { 413 | if (other == TOP || other == POSITIVE) { 414 | return POSITIVE; 415 | } else if (other == ZERO) { 416 | return ZERO; 417 | } else if (other == NEGATIVE) { 418 | return NEGATIVE; 419 | } else { 420 | return BOTTOM; 421 | } 422 | } 423 | 424 | @Override 425 | public Sign negate() { 426 | return NEGATIVE; 427 | } 428 | 429 | @Override 430 | public String toString() { 431 | return "+"; 432 | } 433 | }; 434 | 435 | /** The sign of negative numbers. */ 436 | public static final Sign NEGATIVE = new Sign() { 437 | @Override 438 | public Sign meet(Sign other) { 439 | if (other == TOP || other == NEGATIVE) { 440 | return NEGATIVE; 441 | } else { 442 | return BOTTOM; 443 | } 444 | } 445 | @Override 446 | public Sign plus(Sign other) { 447 | if (other == TOP || other == NEGATIVE || other == ZERO) { 448 | return NEGATIVE; 449 | } else if (other == POSITIVE) { 450 | return BOTTOM; 451 | } else { 452 | return BOTTOM; 453 | } 454 | } 455 | @Override 456 | public Sign mult(Sign other) { 457 | if (other == TOP || other == NEGATIVE) { 458 | return POSITIVE; 459 | } else if (other == ZERO) { 460 | return ZERO; 461 | } else if (other == POSITIVE) { 462 | return NEGATIVE; 463 | } else { 464 | return BOTTOM; 465 | } 466 | } 467 | @Override 468 | public Sign negate() { 469 | return POSITIVE; 470 | } 471 | @Override 472 | public String toString() { 473 | return "-"; 474 | } 475 | }; 476 | } 477 | 478 | } 479 | 480 | 481 | 482 | 483 | -------------------------------------------------------------------------------- /src/test/java/vasco/callgraph/CallGraphTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.callgraph; 19 | 20 | 21 | import java.io.File; 22 | import java.io.FileNotFoundException; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | import java.io.PrintWriter; 26 | import java.util.HashSet; 27 | import java.util.Iterator; 28 | import java.util.LinkedList; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Set; 32 | import java.util.Stack; 33 | 34 | import org.junit.Test; 35 | import soot.PackManager; 36 | import soot.Scene; 37 | import soot.SootMethod; 38 | import soot.Transform; 39 | import soot.Unit; 40 | import soot.jimple.toolkits.callgraph.Edge; 41 | import vasco.CallSite; 42 | import vasco.Context; 43 | import vasco.ContextTransitionTable; 44 | 45 | /** 46 | * A main class for testing call graph construction using a Flow and Context 47 | * Sensitive Points-to Analysis (FCPA). 48 | * 49 | *

Usage: java vasco.callgraph.CallGraphTest [-cp CLASSPATH] [-out DIR] [-k DEPTH] MAIN_CLASS

50 | * 51 | * @author Rohan Padhye 52 | */ 53 | public class CallGraphTest { 54 | 55 | private static String outputDirectory; 56 | 57 | public static void main(String args[]) { 58 | outputDirectory = "."; 59 | String classPath = System.getProperty("java.class.path"); 60 | String mainClass = null; 61 | int callChainDepth = 10; 62 | 63 | /* ------------------- OPTIONS ---------------------- */ 64 | try { 65 | int i=0; 66 | while(true){ 67 | if (args[i].equals("-cp")) { 68 | classPath = args[i+1]; 69 | i += 2; 70 | } else if (args[i].equals("-out")) { 71 | outputDirectory = args[i+1]; 72 | File outDirFile = new File(outputDirectory); 73 | if (outDirFile.exists() == false && outDirFile.mkdirs() == false) { 74 | throw new IOException("Could not make output directory: " + outputDirectory); 75 | } 76 | i += 2; 77 | } else if (args[i].equals("-k")) { 78 | callChainDepth = Integer.parseInt(args[i+1]); 79 | i += 2; 80 | } else { 81 | mainClass = args[i]; 82 | i++; 83 | break; 84 | } 85 | } 86 | if (i != args.length || mainClass == null) 87 | throw new Exception(); 88 | } catch (Exception e) { 89 | System.out.println("Usage: java vasco.callgraph.CallGraphTest [-cp CLASSPATH] [-out DIR] [-k DEPTH] MAIN_CLASS"); 90 | System.exit(1); 91 | } 92 | 93 | /* ------------------- SOOT OPTIONS ---------------------- */ 94 | String[] sootArgs = { 95 | "-cp", classPath, "-pp", 96 | "-w", "-app", 97 | "-keep-line-number", 98 | "-keep-bytecode-offset", 99 | "-p", "cg", "implicit-entry:false", 100 | "-p", "cg.spark", "enabled", 101 | "-p", "cg.spark", "simulate-natives", 102 | "-p", "cg", "safe-forname", 103 | "-p", "cg", "safe-newinstance", 104 | "-main-class", mainClass, 105 | "-f", "none", mainClass 106 | }; 107 | 108 | 109 | /* ------------------- ANALYSIS ---------------------- */ 110 | CallGraphTransformer cgt = new CallGraphTransformer(); 111 | PackManager.v().getPack("wjtp").add(new Transform("wjtp.fcpa", cgt)); 112 | soot.Main.main(sootArgs); 113 | PointsToAnalysis pointsToAnalysis = cgt.getPointsToAnalysis(); 114 | 115 | 116 | 117 | /* ------------------- LOGGING ---------------------- */ 118 | try { 119 | printCallSiteStats(pointsToAnalysis); 120 | printMethodStats(pointsToAnalysis); 121 | dumpCallChainStats(pointsToAnalysis, callChainDepth); 122 | } catch (FileNotFoundException e1) { 123 | System.err.println("Oops! Could not create log file: " + e1.getMessage()); 124 | System.exit(1); 125 | } 126 | 127 | } 128 | 129 | public static List getSparkExplicitEdges(Unit callStmt) { 130 | Iterator edges = Scene.v().getCallGraph().edgesOutOf(callStmt); 131 | List targets = new LinkedList(); 132 | while (edges.hasNext()) { 133 | Edge edge = edges.next(); 134 | if (edge.isExplicit()) { 135 | targets.add(edge.tgt()); 136 | } 137 | } 138 | return targets; 139 | } 140 | 141 | public static List getSparkExplicitEdges(SootMethod sootMethod) { 142 | Iterator edges = Scene.v().getCallGraph().edgesOutOf(sootMethod); 143 | List targets = new LinkedList(); 144 | while (edges.hasNext()) { 145 | Edge edge = edges.next(); 146 | if (edge.isExplicit()) { 147 | targets.add(edge.tgt()); 148 | } 149 | } 150 | return targets; 151 | } 152 | 153 | 154 | private static Set dirtyMethods = new HashSet(); 155 | 156 | private static void markDirty(Unit defaultSite) { 157 | List methods = getSparkExplicitEdges(defaultSite); 158 | while (methods.isEmpty() == false) { 159 | SootMethod method = methods.remove(0); 160 | if (dirtyMethods.contains(method)) { 161 | continue; 162 | } else { 163 | dirtyMethods.add(method); 164 | methods.addAll(getSparkExplicitEdges(method)); 165 | } 166 | } 167 | } 168 | 169 | public static void printCallSiteStats(PointsToAnalysis pta) throws FileNotFoundException { 170 | // Get context-transition table 171 | ContextTransitionTable ctt = pta.getContextTransitionTable(); 172 | Map,Set>> callSitesWithinContexts = ctt.getCallSitesOfContexts(); 173 | Map,Map>> transitions = ctt.getTransitions(); 174 | Set> defaultCallSites = ctt.getDefaultCallSites(); 175 | 176 | // Initialise output stream 177 | PrintWriter csv = new PrintWriter(outputDirectory + "/sites.csv"); 178 | csv.println("FcpaEdges, SparkEdges, Context, CallSite"); 179 | 180 | // The visited set 181 | Set> visited = new HashSet>(); 182 | 183 | // Maintain a stack of contexts to process 184 | Stack> stack = new Stack>(); 185 | 186 | // Initialise it with the main context 187 | Context source = pta.getContexts(Scene.v().getMainMethod()).get(0); 188 | stack.push(source); 189 | 190 | // Now recursively (using stacks) mark reachable contexts 191 | while (stack.isEmpty() == false) { 192 | // Get the next item to process 193 | source = stack.pop(); 194 | // Add successors 195 | if (callSitesWithinContexts.containsKey(source)) { 196 | // The above check is there because methods with no calls have no entry 197 | for (CallSite callSite : callSitesWithinContexts.get(source)) { 198 | // My edges are -1 for "default" sites, and whatever the CTT has otherwise 199 | int myEdges = defaultCallSites.contains(callSite) ? -1 : transitions.get(callSite).size(); 200 | // Get SPARK's edges from the Soot call graph 201 | int sparkEdges = getSparkExplicitEdges(callSite.getCallNode()).size(); 202 | 203 | // Log this 204 | csv.println(myEdges + ", " + sparkEdges + ", " + source + ", " + 205 | "\"" + callSite.getCallNode() + "\""); 206 | 207 | if (myEdges > 0) { 208 | for (SootMethod method : transitions.get(callSite).keySet()) { 209 | Context target = transitions.get(callSite).get(method); 210 | // Don't process the same element twice 211 | if (visited.contains(target) == false) { 212 | // Mark reachable 213 | visited.add(target); 214 | // Add it's successors also later 215 | stack.push(target); 216 | } 217 | } 218 | } else if (myEdges == -1) { 219 | // Default call-site, so mark reachable closure as "dirty" 220 | markDirty(callSite.getCallNode()); 221 | } 222 | } 223 | } 224 | 225 | } 226 | // Close the CSV file 227 | csv.close(); 228 | } 229 | 230 | public static void printMethodStats(PointsToAnalysis pta) throws FileNotFoundException { 231 | // Initialise output stream 232 | PrintWriter csv = new PrintWriter(outputDirectory + "/methods.csv"); 233 | csv.println("Method, Contexts, Application?, Dirty?"); 234 | for (SootMethod method : pta.getMethods()) { 235 | csv.println("\"" + method + "\"" + ", " + pta.getContexts(method).size() + 236 | ", " + (method.getDeclaringClass().isApplicationClass() ? 1 : 0) + 237 | ", " + (dirtyMethods.contains(method) ? 1 : 0)); 238 | } 239 | 240 | // Close the CSV file 241 | csv.close(); 242 | } 243 | 244 | public static void dumpCallChainStats(PointsToAnalysis pta, int maxDepth) throws FileNotFoundException { 245 | // Initialise output stream 246 | PrintWriter txt = new PrintWriter(new FileOutputStream(outputDirectory + "/chains.txt"), true); 247 | Context mainContext = pta.getContexts(Scene.v().getMainMethod()).get(0); 248 | SootMethod mainMethod = Scene.v().getMainMethod(); 249 | 250 | txt.println("FCPA Chains"); 251 | txt.println("------------"); 252 | for(int k=1; k<=maxDepth; k++) { 253 | txt.println("k=" + k + ": " + countCallChains(pta, mainContext, k)); 254 | } 255 | txt.println("Spark Chains"); 256 | txt.println("------------"); 257 | for(int k=1; k<=maxDepth; k++) { 258 | txt.println("k=" + k + ": " + countCallChains(mainMethod, k)); 259 | } 260 | 261 | txt.close(); 262 | 263 | } 264 | 265 | private static long countCallChains(SootMethod method, int k) { 266 | if (k == 0) 267 | return 1; 268 | 269 | long count = 1; 270 | Iterator edges = Scene.v().getCallGraph().edgesOutOf(method); 271 | while(edges.hasNext()) { 272 | Edge edge = edges.next(); 273 | if (edge.isExplicit()) { 274 | SootMethod target = edge.tgt(); 275 | count = count + countCallChains(target, k-1); 276 | } 277 | } 278 | return count; 279 | } 280 | 281 | private static long countCallChains(PointsToAnalysis pta, Context context, int k) { 282 | if (k == 0) 283 | return 1; 284 | 285 | long count = 1; 286 | ContextTransitionTable ctt = pta.getContextTransitionTable(); 287 | if (ctt.getCallSitesOfContexts().containsKey(context)) { 288 | for (CallSite callSite : ctt.getCallSitesOfContexts().get(context)) { 289 | if (ctt.getDefaultCallSites().contains(callSite)) { 290 | Iterator edges = Scene.v().getCallGraph().edgesOutOf(callSite.getCallNode()); 291 | while(edges.hasNext()) { 292 | SootMethod target = edges.next().tgt(); 293 | count = count + countCallChains(target, k-1); 294 | } 295 | } else if (ctt.getTransitions().containsKey(callSite) && ctt.getTransitions().get(callSite) instanceof Map) { 296 | for (Context target : ctt.getTransitions().get(callSite).values()) { 297 | if (target.getMethod().getName().equals("")) { 298 | continue; 299 | } else { 300 | count = count + countCallChains(pta, target, k-1); 301 | } 302 | } 303 | } 304 | } 305 | } 306 | 307 | return count; 308 | 309 | } 310 | 311 | @Test 312 | public void testCallGraphAnalaysis() { 313 | // TODO: Compare output with an ideal (expected) output 314 | CallGraphTest.main(new String[]{"-k", "-3", "-out", "target/test-results/CallGraphTestResults", "vasco.tests.CallGraphTestCase"}); 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/test/java/vasco/soot/examples/CopyConstantTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.soot.examples; 19 | 20 | import java.util.Map; 21 | 22 | import org.junit.Test; 23 | import soot.Local; 24 | import soot.PackManager; 25 | import soot.SceneTransformer; 26 | import soot.SootMethod; 27 | import soot.Transform; 28 | import soot.Unit; 29 | import soot.jimple.Constant; 30 | import vasco.DataFlowSolution; 31 | 32 | /** 33 | * A Soot {@link SceneTransformer} for performing {@link CopyConstantAnalysis}. 34 | * 35 | * @author Rohan Padhye 36 | */ 37 | public class CopyConstantTest extends SceneTransformer { 38 | 39 | private CopyConstantAnalysis analysis; 40 | 41 | 42 | @Override 43 | protected void internalTransform(String arg0, @SuppressWarnings("rawtypes") Map arg1) { 44 | analysis = new CopyConstantAnalysis(); 45 | analysis.doAnalysis(); 46 | DataFlowSolution> solution = analysis.getMeetOverValidPathsSolution(); 47 | System.out.println("----------------------------------------------------------------"); 48 | for (SootMethod sootMethod : analysis.getMethods()) { 49 | System.out.println(sootMethod); 50 | for (Unit unit : sootMethod.getActiveBody().getUnits()) { 51 | System.out.println("----------------------------------------------------------------"); 52 | System.out.println(unit); 53 | System.out.println("IN: " + formatConstants(solution.getValueBefore(unit))); 54 | System.out.println("OUT: " + formatConstants(solution.getValueAfter(unit))); 55 | } 56 | System.out.println("----------------------------------------------------------------"); 57 | } 58 | } 59 | 60 | public static String formatConstants(Map value) { 61 | if (value == null) { 62 | return ""; 63 | } 64 | StringBuffer sb = new StringBuffer(); 65 | for (Map.Entry entry : value.entrySet()) { 66 | Local local = entry.getKey(); 67 | Constant constant = entry.getValue(); 68 | if (constant != null) { 69 | sb.append("(").append(local).append("=").append(constant).append(") "); 70 | } 71 | } 72 | return sb.toString(); 73 | } 74 | 75 | public CopyConstantAnalysis getAnalysis() { 76 | return analysis; 77 | } 78 | 79 | public static void main(String args[]) { 80 | String classPath = System.getProperty("java.class.path"); 81 | String mainClass = null; 82 | 83 | /* ------------------- OPTIONS ---------------------- */ 84 | try { 85 | int i=0; 86 | while(true){ 87 | if (args[i].equals("-cp")) { 88 | classPath = args[i+1]; 89 | i += 2; 90 | } else { 91 | mainClass = args[i]; 92 | i++; 93 | break; 94 | } 95 | } 96 | if (i != args.length || mainClass == null) 97 | throw new Exception(); 98 | } catch (Exception e) { 99 | System.err.println("Usage: java CopyConstantTest [-cp CLASSPATH] MAIN_CLASS"); 100 | System.exit(1); 101 | } 102 | 103 | String[] sootArgs = { 104 | "-cp", classPath, "-pp", 105 | "-w", "-app", 106 | "-keep-line-number", 107 | "-keep-bytecode-offset", 108 | "-p", "jb", "use-original-names", 109 | "-p", "cg", "implicit-entry:false", 110 | "-p", "cg.spark", "enabled", 111 | "-p", "cg.spark", "simulate-natives", 112 | "-p", "cg", "safe-forname", 113 | "-p", "cg", "safe-newinstance", 114 | "-main-class", mainClass, 115 | "-f", "none", mainClass 116 | }; 117 | CopyConstantTest ccp = new CopyConstantTest(); 118 | PackManager.v().getPack("wjtp").add(new Transform("wjtp.ccp", ccp)); 119 | soot.Main.main(sootArgs); 120 | } 121 | 122 | @Test 123 | public void testCopyConstantAnalysis() { 124 | // TODO: Compare output with an ideal (expected) output 125 | CopyConstantTest.main(new String[]{"vasco.tests.CopyConstantTestCase"}); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/vasco/soot/examples/SignTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Rohan Padhye 3 | * 4 | * This library is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as 6 | * published by the Free Software Foundation, either version 2.1 of the 7 | * License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this program. If not, see . 16 | * 17 | */ 18 | package vasco.soot.examples; 19 | 20 | import java.util.Map; 21 | 22 | import org.junit.Test; 23 | import soot.Local; 24 | import soot.PackManager; 25 | import soot.SceneTransformer; 26 | import soot.SootMethod; 27 | import soot.Transform; 28 | import soot.Unit; 29 | import vasco.DataFlowSolution; 30 | import vasco.soot.examples.SignAnalysis.Sign; 31 | 32 | /** 33 | * A Soot {@link SceneTransformer} for performing {@link SignAnalysis}. 34 | * 35 | * @author Rohan Padhye 36 | */ 37 | public class SignTest extends SceneTransformer { 38 | 39 | private SignAnalysis analysis; 40 | 41 | 42 | @Override 43 | protected void internalTransform(String arg0, @SuppressWarnings("rawtypes") Map arg1) { 44 | analysis = new SignAnalysis(); 45 | analysis.doAnalysis(); 46 | DataFlowSolution> solution = analysis.getMeetOverValidPathsSolution(); 47 | System.out.println("----------------------------------------------------------------"); 48 | for (SootMethod sootMethod : analysis.getMethods()) { 49 | System.out.println(sootMethod); 50 | for (Unit unit : sootMethod.getActiveBody().getUnits()) { 51 | System.out.println("----------------------------------------------------------------"); 52 | System.out.println(unit); 53 | System.out.println("IN: " + formatConstants(solution.getValueBefore(unit))); 54 | System.out.println("OUT: " + formatConstants(solution.getValueAfter(unit))); 55 | } 56 | System.out.println("----------------------------------------------------------------"); 57 | } 58 | } 59 | 60 | public static String formatConstants(Map value) { 61 | if (value == null) { 62 | return ""; 63 | } 64 | StringBuffer sb = new StringBuffer(); 65 | for (Map.Entry entry : value.entrySet()) { 66 | Local local = entry.getKey(); 67 | Sign sign = entry.getValue(); 68 | if (sign != null) { 69 | sb.append("(").append(local).append(": ").append(sign.toString()).append(") "); 70 | } 71 | } 72 | return sb.toString(); 73 | } 74 | 75 | public SignAnalysis getAnalysis() { 76 | return analysis; 77 | } 78 | 79 | public static void main(String args[]) { 80 | String classPath = System.getProperty("java.class.path"); 81 | String mainClass = null; 82 | 83 | /* ------------------- OPTIONS ---------------------- */ 84 | try { 85 | int i=0; 86 | while(true){ 87 | if (args[i].equals("-cp")) { 88 | classPath = args[i+1]; 89 | i += 2; 90 | } else { 91 | mainClass = args[i]; 92 | i++; 93 | break; 94 | } 95 | } 96 | if (i != args.length || mainClass == null) 97 | throw new Exception(); 98 | } catch (Exception e) { 99 | System.err.println("Usage: java SignTest [-cp CLASSPATH] MAIN_CLASS"); 100 | System.exit(1); 101 | } 102 | 103 | String[] sootArgs = { 104 | "-cp", classPath, "-pp", 105 | "-w", "-app", 106 | "-keep-line-number", 107 | "-keep-bytecode-offset", 108 | "-p", "jb", "use-original-names", 109 | "-p", "cg", "implicit-entry:false", 110 | "-p", "cg.spark", "enabled", 111 | "-p", "cg.spark", "simulate-natives", 112 | "-p", "cg", "safe-forname", 113 | "-p", "cg", "safe-newinstance", 114 | "-main-class", mainClass, 115 | "-f", "none", mainClass 116 | }; 117 | SignTest sgn = new SignTest(); 118 | PackManager.v().getPack("wjtp").add(new Transform("wjtp.sgn", sgn)); 119 | soot.Main.main(sootArgs); 120 | } 121 | 122 | @Test 123 | public void testSignAnalysis() { 124 | // TODO: Compare output with an ideal (expected) output 125 | SignTest.main(new String[]{"vasco.tests.SignTestCase"}); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/vasco/tests/CallGraphTestCase.java: -------------------------------------------------------------------------------- 1 | package vasco.tests; 2 | 3 | public class CallGraphTestCase { 4 | 5 | static class A { 6 | void foo() { bar(); } 7 | void bar() { } 8 | } 9 | 10 | public static void main(String[] args) { 11 | A a1 = new A(); 12 | a1.foo(); 13 | 14 | A a2 = new A(); 15 | a2.foo(); 16 | 17 | a2.bar(); 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/vasco/tests/CopyConstantTestCase.java: -------------------------------------------------------------------------------- 1 | package vasco.tests; 2 | 3 | public class CopyConstantTestCase { 4 | public static void main(String... args) { 5 | Inner in = new Inner(8); 6 | int x = in.sq(); 7 | int y = in.val(); 8 | int z = in.foo(8, 8); 9 | System.out.println(x+y+z); 10 | } 11 | 12 | private static class Inner { 13 | int data; 14 | 15 | Inner(int val) { 16 | this.data = val; 17 | } 18 | 19 | public int sq() { 20 | return data*data; 21 | } 22 | 23 | public int val() { 24 | return this.data; 25 | } 26 | 27 | public int foo(int a, int b) { 28 | int x = a; 29 | int y = b; 30 | int z; 31 | if (a < 5) { 32 | z = x; 33 | } else { 34 | z = y; 35 | } 36 | return z; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/vasco/tests/SignTestCase.java: -------------------------------------------------------------------------------- 1 | package vasco.tests; 2 | 3 | public class SignTestCase { 4 | static int P, Q, R; 5 | 6 | public static void main(String... args) { 7 | int p = five(); 8 | int q = f(p, -3); 9 | int r = g(-q); 10 | P = p; 11 | Q = q; 12 | R = r; 13 | } 14 | 15 | public static int five() { 16 | return 5; 17 | } 18 | 19 | public static int f(int a, int b) { 20 | int c; 21 | if (a < b) { 22 | c = a * b; 23 | } else { 24 | c = g(10); 25 | } 26 | return c; 27 | } 28 | 29 | public static int g(int u) { 30 | int v = f(-u, u); 31 | return v; 32 | } 33 | } 34 | --------------------------------------------------------------------------------