-
Notifications
You must be signed in to change notification settings - Fork 300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The results of running FlowDroid multiple times are inconsistent. #790
Comments
The IFDS-native result representation is a taint graph with predecessors and neighbors (incoming abstraction that is already in the graph, e.g., after if else). Because the IFDS solver is highly parallelized, the order in which edges are discovered is non-deterministic. So in one execution, the graph might contain the abstraction from the condition=true path and the neighbor from the condition=false path, and in another vice versa. Afterwards, this graph is traversed to build a concrete path through the program (see Furthermore, there seems to be another problem in your case. It seems that the number of sources differs between different runs ( |
Thank you for your answer. |
To give you some more background on why we report a single witness and not all paths, take the following code example:
In this example, there are 3 leaking paths:
In the general case, if you have N conditionals, there might be 2^n paths. This can lead to a path explosion quickly and the analysis would no longer terminate in any realistic timeframe. |
Thank you for your detailed explanations, which have given me a deeper understanding of FlowDroid. However, I still have a small question. Every time I run the analysis, the final result, |
Are the numbers also different in the source lookup log?
The easiest would be if you could provide the options and sources/sinks you used and the jar under analysis. |
I'm sorry if my explanation was a bit unclear. I modified a part of FlowDroid and used it to analyze a Java Web application. My entry point for the analysis is a self-constructed virtual main method. In the program, I only defined one source and one sink, and during the process of finding the source and sink, it only identified one of each: Later, when tracking back from the abstraction that reaches the sink to find the propagation path to the source, the paths found are different each time the program is executed. The log looks as follows: My understanding is that these two logs have the following meanings:
Is my understanding correct? |
Yep.
This is not sufficient to tell whether the callgraph is the same. But if the callgraph had a bug, both lines would be unstable as FlowDroid only searches for sources and sinks in the reachable methods. That's why I've asked. Another log line that might help you is
If F or B is different between runs, your main method changes or you found a non-determinism inside the data-flow analysis. If R changes between runs, you might have a problem with a buggy |
I run the same explame three times,and following are the key logs from each run.
|
The concept is as follows. FlowDroid first identifies all source statements and injects taint abstractions there. These taints are then propagated through the interprocedural control flow graph using IFDS. During propagation, each new taint has a reference to its predecessor. That means, while every taint knows its predecessor, it does not know the source from which it came. Once the IFDS phase has completed, we have a set of taints that have arrived at sink statements. The path reconstructor now traverses the taint graph backwards via the predecessor links to reconstruct a path that ends at a source. Starting at one sink, there may be many paths to many different sources. If the path reconstructor hits a timeout, it will stop. If you don't need the paths and you're only interested in which source is connected to which sink, you can use the Keep in mind that the IFDS phase is always context-sensitive. Ignoring contexts during path reconstruction only leads to a false positive if multiple flows overlap with different contexts in the same method. There are examples, but it doesn't mean that you have an overall context-insensitive analysis. It's still pretty good for many cases. |
Dear Dr. Arzt,
I have observed a phenomenon but am unsure of the exact cause.I used the core analysis engine of FlowDroid to run a Java program and found that the results were inconsistent across multiple runs.After an Abstraction reaches the sink point and begins the process of tracing back the call chain, it produces different results, even though the Abstraction that reaches the sink point is the same.
The structure of the Abstraction that reaches the sink is:
Current Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Predecessor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
Current Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Predecessor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
Current Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: r1(javax.servlet.ServletRequest) * <+length> | >>
-> Predecessor Abstraction: $r2(javax.servlet.ServletRequest) * <+length> | >>
Current Abstraction: $r2(javax.servlet.ServletRequest) * <+length> | >>
-> Neighbor Abstraction: $r2(javax.servlet.ServletRequest) * <+length> | >>
-> Predecessor Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
Current Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Neighbor Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Neighbor Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Neighbor Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Neighbor Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Predecessor Abstraction: $r3(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
Current Abstraction: $r3(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Predecessor Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
Current Abstraction: r0(org.apache.catalina.core.ApplicationFilterChain$1) <org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> * <+length> | >>
-> Predecessor Abstraction: r2(org.apache.catalina.connector.RequestFacade) * <+length> | >>
Current Abstraction: r2(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Predecessor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
Current Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Neighbor Abstraction: r1(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Predecessor Abstraction: servletrequest(org.apache.catalina.connector.RequestFacade) * <+length> | >>
Current Abstraction: servletrequest(org.apache.catalina.connector.RequestFacade) * <+length> | >>
-> Predecessor Abstraction: request(org.apache.catalina.connector.RequestFacade) * <+length> | >>
Current Abstraction: request(org.apache.catalina.connector.RequestFacade) * <+length> | >>
No more predecessors.
The execution results are as follows:
1:
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - on Path:
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <dummyMainClass: void dummyMainMethod(java.lang.String[])>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> request = virtualinvoke $r7.<org.apache.catalina.connector.Request: javax.servlet.http.HttpServletRequest getRequest()>()
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <dummyMainClass: void dummyMainMethod(java.lang.String[])>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> servletrequest = (javax.servlet.ServletRequest) request
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <dummyMainClass: void dummyMainMethod(java.lang.String[])>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> virtualinvoke $r2.<org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(servletrequest, servletresponse)
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> specialinvoke $r3.<org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r0, r1, r2)
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> r0.<org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req> = r2
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> return
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> staticinvoke <java.security.AccessController: java.lang.Object doPrivileged(java.security.PrivilegedExceptionAction)>($r3)
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain$1: java.lang.Void run()>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> $r2 = r0.<org.apache.catalina.core.ApplicationFilterChain$1: javax.servlet.ServletRequest val$req>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain$1: java.lang.Void run()>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> staticinvoke <org.apache.catalina.core.ApplicationFilterChain: void access$000(org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>($r3, $r2, $r1)
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void access$000(org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> specialinvoke r0.<org.apache.catalina.core.ApplicationFilterChain: void internalDoFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r1, r2)
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void internalDoFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - -> interfaceinvoke $r38.<javax.servlet.Servlet: void service(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r1, r2)
09:36:24.639 [main] INFO auth.jarInflow.JarAuthInfoflow - Data flow solver took 4 seconds. Maximum memory consumption: 1444 MB
09:36:24.640 [main] INFO SetupApplication - Found 1 leaks from 29 sources
2:
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - on Path:
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <dummyMainClass: void dummyMainMethod(java.lang.String[])>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> request = virtualinvoke $r7.<org.apache.catalina.connector.Request: javax.servlet.http.HttpServletRequest getRequest()>()
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <dummyMainClass: void dummyMainMethod(java.lang.String[])>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> servletrequest = (javax.servlet.ServletRequest) request
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <dummyMainClass: void dummyMainMethod(java.lang.String[])>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> virtualinvoke $r2.<org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(servletrequest, servletresponse)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> specialinvoke $r3.<org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r0, r1, r2)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> return
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> specialinvoke r0.<org.apache.catalina.core.ApplicationFilterChain: void internalDoFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r1, r2)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void internalDoFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> interfaceinvoke r35.<javax.servlet.Filter: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse,javax.servlet.FilterChain)>(r1, r2, r0)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <BOOT-INF.classes.com.jsh.erp.filter.LogCostFilter: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse,javax.servlet.FilterChain)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> interfaceinvoke r13.<javax.servlet.FilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r0, r2)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> specialinvoke $r3.<org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r0, r1, r2)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain$1: void (org.apache.catalina.core.ApplicationFilterChain,javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> return
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void doFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> specialinvoke r0.<org.apache.catalina.core.ApplicationFilterChain: void internalDoFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r1, r2)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> <org.apache.catalina.core.ApplicationFilterChain: void internalDoFilter(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - -> interfaceinvoke $r38.<javax.servlet.Servlet: void service(javax.servlet.ServletRequest,javax.servlet.ServletResponse)>(r1, r2)
09:40:12.596 [main] INFO auth.jarInflow.JarAuthInfoflow - Data flow solver took 5 seconds. Maximum memory consumption: 1233 MB
09:40:12.597 [main] INFO SetupApplication - Found 1 leaks from 9 sources
The text was updated successfully, but these errors were encountered: