Java8 invokevirtual resolution should search into super interfaces

Summary:
Since Java8, interfaces mays contain implementations
(default methods). We modify the resolve algorith in the Java frontend
to take care of that.

Reviewed By: jvillard

Differential Revision: D21785182

fbshipit-source-id: ffab8124c
master
David Pichardie 5 years ago committed by Facebook GitHub Bot
parent 1459505540
commit dbdd4413a7

@ -599,28 +599,47 @@ let rec expression (context : JContext.t) pc expr =
let method_invocation (context : JContext.t) loc pc var_opt cn ms sil_obj_opt expr_list invoke_code let method_invocation (context : JContext.t) loc pc var_opt cn ms sil_obj_opt expr_list invoke_code
method_kind = method_kind =
(* This function tries to recursively search for the classname of the class *) (* This function tries to recursively search for the classname of the class
(* where the method is defined. It returns the classname given as argument*) where the method is defined. Following Java8 invokevirtual spec, it
(* when this classname cannot be found *) searches first in parent classes. Then, if nothing is found, it searches in super
interfaces. If nothing is found, it returns the classname given as argument. *)
let contains_ms_implem node ms =
match node with
| Javalib.JInterface {i_methods= mmap} | Javalib.JClass {c_methods= mmap} ->
if JBasics.MethodMap.mem ms mmap then
match JBasics.MethodMap.find ms mmap with
| Javalib.AbstractMethod _ ->
false
| Javalib.ConcreteMethod _ ->
true
else false
in
let resolve_method (context : JContext.t) cn ms = let resolve_method (context : JContext.t) cn ms =
let rec loop fallback_cn cn = let rec search_in_parents get_parents cn =
match JClasspath.lookup_node cn context.program with match JClasspath.lookup_node cn context.program with
| None -> | None ->
fallback_cn None
| Some node -> ( | Some node ->
if Javalib.defines_method node ms then cn if contains_ms_implem node ms then Some cn
else else List.find_map (get_parents node) ~f:(search_in_parents get_parents)
match node with in
let super_classes = function
| Javalib.JInterface _ -> | Javalib.JInterface _ ->
fallback_cn []
| Javalib.JClass jclass -> ( | Javalib.JClass {c_super_class} ->
match jclass.Javalib.c_super_class with Option.to_list c_super_class
| None -> in
fallback_cn let super_interfaces = function
| Some super_cn -> | Javalib.JInterface {i_interfaces} ->
loop fallback_cn super_cn ) ) i_interfaces
| Javalib.JClass {c_interfaces} ->
c_interfaces
in in
loop cn cn match search_in_parents super_classes cn with
| None ->
Option.value ~default:cn (search_in_parents super_interfaces cn)
| Some cn_implem ->
cn_implem
in in
let cn' = resolve_method context cn ms in let cn' = resolve_method context cn ms in
let tenv = JContext.get_tenv context in let tenv = JContext.get_tenv context in

@ -0,0 +1,27 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
interface I {
default Object defaultMethod1() {
return null;
}
default Object defaultMethod2() {
return "foo";
}
}
public class DefaultInInterface implements I {
public void bad() {
System.out.println(this.defaultMethod1().toString());
}
public void ok() {
System.out.println(this.defaultMethod2().toString());
}
}

@ -38,6 +38,7 @@ codetoanalyze/java/biabduction/CursorLeaks.java, codetoanalyze.java.infer.Cursor
codetoanalyze/java/biabduction/CursorNPEs.java, codetoanalyze.java.infer.CursorNPEs.cursorFromContentResolverNPE(java.lang.String):void, 8, NULL_DEREFERENCE, B1, ERROR, [start of procedure cursorFromContentResolverNPE(...)] codetoanalyze/java/biabduction/CursorNPEs.java, codetoanalyze.java.infer.CursorNPEs.cursorFromContentResolverNPE(java.lang.String):void, 8, NULL_DEREFERENCE, B1, ERROR, [start of procedure cursorFromContentResolverNPE(...)]
codetoanalyze/java/biabduction/CursorNPEs.java, codetoanalyze.java.infer.CursorNPEs.cursorFromDownloadManagerNPE(android.app.DownloadManager):int, 5, NULL_DEREFERENCE, B2, ERROR, [start of procedure cursorFromDownloadManagerNPE(...)] codetoanalyze/java/biabduction/CursorNPEs.java, codetoanalyze.java.infer.CursorNPEs.cursorFromDownloadManagerNPE(android.app.DownloadManager):int, 5, NULL_DEREFERENCE, B2, ERROR, [start of procedure cursorFromDownloadManagerNPE(...)]
codetoanalyze/java/biabduction/CursorNPEs.java, codetoanalyze.java.infer.CursorNPEs.cursorFromMediaNPE():void, 2, NULL_DEREFERENCE, B1, ERROR, [start of procedure cursorFromMediaNPE()] codetoanalyze/java/biabduction/CursorNPEs.java, codetoanalyze.java.infer.CursorNPEs.cursorFromMediaNPE():void, 2, NULL_DEREFERENCE, B1, ERROR, [start of procedure cursorFromMediaNPE()]
codetoanalyze/java/biabduction/DefaultInInterface.java, DefaultInInterface.bad():void, 1, NULL_DEREFERENCE, B1, ERROR, [start of procedure bad(),start of procedure defaultMethod1(),return from a call to Object I.defaultMethod1()]
codetoanalyze/java/biabduction/DivideByZero.java, codetoanalyze.java.infer.DivideByZero.callDivideByZeroInterProc():int, 1, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure callDivideByZeroInterProc(),start of procedure divideByZeroInterProc(...)] codetoanalyze/java/biabduction/DivideByZero.java, codetoanalyze.java.infer.DivideByZero.callDivideByZeroInterProc():int, 1, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure callDivideByZeroInterProc(),start of procedure divideByZeroInterProc(...)]
codetoanalyze/java/biabduction/DivideByZero.java, codetoanalyze.java.infer.DivideByZero.divByZeroLocal(java.lang.String):int, 3, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure divByZeroLocal(...)] codetoanalyze/java/biabduction/DivideByZero.java, codetoanalyze.java.infer.DivideByZero.divByZeroLocal(java.lang.String):int, 3, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure divByZeroLocal(...)]
codetoanalyze/java/biabduction/DivideByZero.java, codetoanalyze.java.infer.DivideByZero.divideByZeroWithStaticField():int, 2, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure divideByZeroWithStaticField(),start of procedure setXToZero(),return from a call to void DivideByZero.setXToZero(),start of procedure divideByZeroInterProc(...)] codetoanalyze/java/biabduction/DivideByZero.java, codetoanalyze.java.infer.DivideByZero.divideByZeroWithStaticField():int, 2, DIVIDE_BY_ZERO, no_bucket, ERROR, [start of procedure divideByZeroWithStaticField(),start of procedure setXToZero(),return from a call to void DivideByZero.setXToZero(),start of procedure divideByZeroInterProc(...)]

@ -106,7 +106,7 @@ codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.ex
codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.reuseIntentBad(android.app.Activity):void, 1, INSECURE_INTENT_HANDLING, no_bucket, ERROR, [Return from Intent Activity.getIntent(),Call to void Activity.startActivity(Intent) with tainted index 1] codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.reuseIntentBad(android.app.Activity):void, 1, INSECURE_INTENT_HANDLING, no_bucket, ERROR, [Return from Intent Activity.getIntent(),Call to void Activity.startActivity(Intent) with tainted index 1]
codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.startWithUri1Bad(android.net.Uri):void, 1, CREATE_INTENT_FROM_URI, no_bucket, ERROR, [Return from Intent.<init>(String,Uri),Call to void Activity.startActivity(Intent) with tainted index 1] codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.startWithUri1Bad(android.net.Uri):void, 1, CREATE_INTENT_FROM_URI, no_bucket, ERROR, [Return from Intent.<init>(String,Uri),Call to void Activity.startActivity(Intent) with tainted index 1]
codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.startWithUri2Bad(android.net.Uri):void, 1, CREATE_INTENT_FROM_URI, no_bucket, ERROR, [Return from Intent.<init>(String,Uri,Context,Class),Call to void Activity.startActivity(Intent) with tainted index 1] codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.startWithUri2Bad(android.net.Uri):void, 1, CREATE_INTENT_FROM_URI, no_bucket, ERROR, [Return from Intent.<init>(String,Uri,Context,Class),Call to void Activity.startActivity(Intent) with tainted index 1]
codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.subclassCallBad(codetoanalyze.java.quandary.IntentSubclass,codetoanalyze.java.quandary.ContextSubclass):void, 3, QUANDARY_TAINT_ERROR, no_bucket, ERROR, [Return from Object InferTaint.inferSecretSource(),Call to void Context.startActivity(Intent) with tainted index 1] codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.Intents.subclassCallBad(codetoanalyze.java.quandary.IntentSubclass,codetoanalyze.java.quandary.ContextSubclass):void, 3, QUANDARY_TAINT_ERROR, no_bucket, ERROR, [Return from Object InferTaint.inferSecretSource(),Call to void ContextSubclass.startActivity(Intent) with tainted index 1]
codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.MyActivity.startServiceWithTaintedIntent():void, 2, CREATE_INTENT_FROM_URI, no_bucket, ERROR, [Return from Intent.<init>(String,Uri),Call to ComponentName ContextWrapper.startService(Intent) with tainted index 1] codetoanalyze/java/quandary/Intents.java, codetoanalyze.java.quandary.MyActivity.startServiceWithTaintedIntent():void, 2, CREATE_INTENT_FROM_URI, no_bucket, ERROR, [Return from Intent.<init>(String,Uri),Call to ComponentName ContextWrapper.startService(Intent) with tainted index 1]
codetoanalyze/java/quandary/Interprocedural.java, codetoanalyze.java.quandary.Interprocedural.FP_divergenceInCallee():void, 3, QUANDARY_TAINT_ERROR, no_bucket, ERROR, [Return from Object InferTaint.inferSecretSource(),Call to void InferTaint.inferSensitiveSink(Object) with tainted index 0] codetoanalyze/java/quandary/Interprocedural.java, codetoanalyze.java.quandary.Interprocedural.FP_divergenceInCallee():void, 3, QUANDARY_TAINT_ERROR, no_bucket, ERROR, [Return from Object InferTaint.inferSecretSource(),Call to void InferTaint.inferSensitiveSink(Object) with tainted index 0]
codetoanalyze/java/quandary/Interprocedural.java, codetoanalyze.java.quandary.Interprocedural.FP_reassignInCallee():void, 4, QUANDARY_TAINT_ERROR, no_bucket, ERROR, [Return from Object InferTaint.inferSecretSource(),Call to void InferTaint.inferSensitiveSink(Object) with tainted index 0] codetoanalyze/java/quandary/Interprocedural.java, codetoanalyze.java.quandary.Interprocedural.FP_reassignInCallee():void, 4, QUANDARY_TAINT_ERROR, no_bucket, ERROR, [Return from Object InferTaint.inferSecretSource(),Call to void InferTaint.inferSensitiveSink(Object) with tainted index 0]

Loading…
Cancel
Save