fix: improve checking of access modifiers for classes (PR #2251)

This commit is contained in:
pubiqq 2024-08-15 21:59:44 +03:00 committed by GitHub
parent 0be5b2cea9
commit 9a8ec76989
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 95 additions and 28 deletions

View File

@ -40,6 +40,7 @@ import jadx.core.dex.info.InfoStorage;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.info.PackageInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.utils.ClassUtils;
import jadx.core.dex.nodes.utils.MethodUtils;
import jadx.core.dex.nodes.utils.TypeUtils;
import jadx.core.dex.visitors.DepthTraversal;
@ -71,6 +72,7 @@ public class RootNode {
private final InfoStorage infoStorage = new InfoStorage();
private final CacheStorage cacheStorage = new CacheStorage();
private final TypeUpdate typeUpdate;
private final ClassUtils classUtils;
private final MethodUtils methodUtils;
private final TypeUtils typeUtils;
private final AttributeStorage attributes = new AttributeStorage();
@ -104,6 +106,7 @@ public class RootNode {
this.stringUtils = new StringUtils(args);
this.constValues = new ConstStorage(args);
this.typeUpdate = new TypeUpdate(this);
this.classUtils = new ClassUtils(this);
this.methodUtils = new MethodUtils(this);
this.typeUtils = new TypeUtils(this);
}
@ -704,6 +707,10 @@ public class RootNode {
return args.getCodeCache();
}
public ClassUtils getClassUtils() {
return classUtils;
}
public MethodUtils getMethodUtils() {
return methodUtils;
}

View File

@ -0,0 +1,57 @@
package jadx.core.dex.nodes.utils;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
public class ClassUtils {
private final RootNode root;
public ClassUtils(RootNode rootNode) {
this.root = rootNode;
}
public boolean isAccessible(ClassNode cls, ClassNode callerCls) {
if (cls.equals(callerCls)) {
return true;
}
final AccessInfo accessFlags = cls.getAccessFlags();
if (accessFlags.isPublic()) {
return true;
}
if (accessFlags.isProtected()) {
return isProtectedAccessible(cls, callerCls);
}
if (accessFlags.isPackagePrivate()) {
return isPackagePrivateAccessible(cls, callerCls);
}
if (accessFlags.isPrivate()) {
return isPrivateAccessible(cls, callerCls);
}
throw new JadxRuntimeException(accessFlags + " is not supported");
}
private boolean isProtectedAccessible(ClassNode cls, ClassNode callerCls) {
return isPackagePrivateAccessible(cls, callerCls) || isSuperType(cls, callerCls);
}
private boolean isPackagePrivateAccessible(ClassNode cls, ClassNode callerCls) {
return cls.getPackageNode().equals(callerCls.getPackageNode());
}
private boolean isPrivateAccessible(ClassNode cls, ClassNode callerCls) {
return cls.getTopParentClass().equals(callerCls.getTopParentClass());
}
private boolean isSuperType(ClassNode cls, ClassNode superCls) {
return root.getClsp().getSuperTypes(cls.getRawName()).stream()
.anyMatch(x -> x.equals(superCls.getRawName()));
}
}

View File

@ -1,5 +1,8 @@
package jadx.core.dex.visitors;
import java.util.Set;
import java.util.stream.Collectors;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
@ -11,6 +14,7 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.IMethodDetails;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.nodes.utils.ClassUtils;
import jadx.core.utils.exceptions.JadxException;
@JadxVisitor(
@ -20,10 +24,13 @@ import jadx.core.utils.exceptions.JadxException;
)
public class FixAccessModifiers extends AbstractVisitor {
private ClassUtils classUtils;
private boolean respectAccessModifiers;
@Override
public void init(RootNode root) {
this.classUtils = root.getClassUtils();
this.respectAccessModifiers = root.getArgs().isRespectBytecodeAccModifiers();
}
@ -60,43 +67,39 @@ public class FixAccessModifiers extends AbstractVisitor {
}
private int fixClassVisibility(ClassNode cls) {
if (cls.getUseIn().isEmpty()) {
AccessInfo accessFlags = cls.getAccessFlags();
if (accessFlags.isPublic()) {
return -1;
}
AccessInfo accessFlags = cls.getAccessFlags();
if (accessFlags.isPrivate()) {
if (!cls.isInner()) {
if (cls.isTopClass() && (accessFlags.isPrivate() || accessFlags.isProtected())) {
return AccessFlags.PUBLIC;
}
for (ClassNode useCls : cls.getUseIn()) {
if (!classUtils.isAccessible(cls, useCls)) {
return AccessFlags.PUBLIC;
}
// check if private inner class is used outside
ClassNode topParentClass = cls.getTopParentClass();
for (ClassNode useCls : cls.getUseIn()) {
if (useCls.getTopParentClass() != topParentClass) {
return AccessFlags.PUBLIC;
}
}
}
if (accessFlags.isPackagePrivate()) {
String pkg = cls.getPackage();
for (ClassNode useCls : cls.getUseIn()) {
if (!useCls.getPackage().equals(pkg)) {
return AccessFlags.PUBLIC;
}
}
}
if (!accessFlags.isPublic()) {
// if class is used in inlinable method => make it public
for (MethodNode useMth : cls.getUseInMth()) {
MethodInlineAttr inlineAttr = useMth.get(AType.METHOD_INLINE);
boolean isInline = inlineAttr != null && !inlineAttr.notNeeded();
boolean isCandidateForInline = useMth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE);
boolean mostLikelyInline = isInline || isCandidateForInline;
if (mostLikelyInline && !useMth.getUseIn().isEmpty()) {
return AccessFlags.PUBLIC;
for (MethodNode useMth : cls.getUseInMth()) {
MethodInlineAttr inlineAttr = useMth.get(AType.METHOD_INLINE);
boolean isInline = inlineAttr != null && !inlineAttr.notNeeded();
boolean isCandidateForInline = useMth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE);
if (isInline || isCandidateForInline) {
Set<ClassNode> usedInClss = useMth.getUseIn().stream()
.map(MethodNode::getParentClass)
.collect(Collectors.toSet());
for (ClassNode useCls : usedInClss) {
if (!classUtils.isAccessible(cls, useCls)) {
return AccessFlags.PUBLIC;
}
}
}
}
return -1;
}